From 495336fc5845b93b6b5c4ecffd7f4f44fb61af69 Mon Sep 17 00:00:00 2001 From: Jonathan Bernard Date: Fri, 28 Jan 2011 06:49:47 -0600 Subject: [PATCH] Starting DB implementation. Added Makefile, id_counter.erl, and yaws.conf. Created ts_common module. Will contain common DB code. So far only list/3. Created ts_timeline module. DB code for storing timeline entries. Created ts_entry module, DB code for storing timelin entries. --- Makefile | 38 ++++++++++++++++++++++++++++++++++++++ src/id_counter.erl | 23 +++++++++++++++++++++++ src/ts_common.erl | 29 +++++++++++++++++++++++++++++ src/ts_db_records.hrl | 12 ++++++++++++ src/ts_entry.erl | 19 +++++++++++++++++++ src/ts_timeline.erl | 34 ++++++++++++++++++++++++++++++++++ yaws.conf | 10 ++++++++++ 7 files changed, 165 insertions(+) create mode 100644 Makefile create mode 100644 src/id_counter.erl create mode 100644 src/ts_common.erl create mode 100644 src/ts_db_records.hrl create mode 100644 src/ts_entry.erl create mode 100644 src/ts_timeline.erl create mode 100644 yaws.conf diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6ff56b2 --- /dev/null +++ b/Makefile @@ -0,0 +1,38 @@ +MODS = $(wildcard src/*.erl) +BEAMS = $(MODS:src/%.erl=ebin/%.beam) +TEST_MODS = $(wildcard test/*.erl) +TEST_BEAMS = $(TEST_MODS:test/%.erl=test/%.beam) + +all : compile test + +compile : $(BEAMS) + +compile-test : $(TEST_BEAMS) + +test : start-test-server run-test stop-test-server + +test-shell : compile compile-test + @echo Starting an interactive YAWS shell with test paths loaded. + @yaws -i --pa test --id test_inst + +run-test : compile compile-test + @erl -pa ./ebin -pa ./test -run timestamper_api_tests test -run init stop -noshell + +start-test-server : + @yaws -D --id test_inst + +stop-test-server : + @yaws --stop --id test_inst + +clean: + rm -rf ebin/* erl_crash.dump test/*.beam + +init: + -mkdir ebin + +ebin/%.beam : src/%.erl + erlc -W -o ebin $< + +test/%.beam : test/%.erl + @echo Compiling sources... + erlc -W -o test $< diff --git a/src/id_counter.erl b/src/id_counter.erl new file mode 100644 index 0000000..442d2d9 --- /dev/null +++ b/src/id_counter.erl @@ -0,0 +1,23 @@ +-module(id_counter). +-export([create_table/1, next_counter/1, dirty_next_counter/1]). + +-include("vbs_db_records.hrl"). + +%% Create the table structure for Mnesia +create_table(Opts) -> + mnesia:create_table(id_counter, Opts ++ + [{attributes, record_info(fields, id_counter)}]). + +%% Get the next id for a given name +next_counter(Name) -> + Rec = case mnesia:read({id_counter, Name}) of + [] -> #id_counter{name=Name, next_value=0}; + [Val] -> Val + end, + NextRec = Rec#id_counter{next_value = Rec#id_counter.next_value+ 1}, + ok = mnesia:write(NextRec), + Rec#id_counter.next_value. + +%% Get the next id for a given name +dirty_next_counter(Name) -> + mnesia:dirty_update_counter(id_counter, Name, 1) - 1. diff --git a/src/ts_common.erl b/src/ts_common.erl new file mode 100644 index 0000000..bb5c6d0 --- /dev/null +++ b/src/ts_common.erl @@ -0,0 +1,29 @@ +-module(ts_common). +-export([list/3]). + +-include_lib("stdlib/include/qlc.hrl"). + +%% list number of records, skipping the first +list(Table, Start, Length) +when is_atom(Table) and is_integer(Start) and is_integer(Length) -> + list(qlc:q([A || A <- mnesia:table(Table)]), Start, Length); + +list(Query, Start, Length) -> + {atomic, Result} = mnesia:transaction(fun() -> + % create a cursor for the query + C = qlc:cursor(Query), + + % skip the first Start records + if Start > 0 -> qlc:next_answers(C, Start); + true -> ok + end, + + % return Length records + List = qlc:next_answers(C, Length), + + % free cursor + ok = qlc:delete_cursor(C), + + List + end), + Result. diff --git a/src/ts_db_records.hrl b/src/ts_db_records.hrl new file mode 100644 index 0000000..989d526 --- /dev/null +++ b/src/ts_db_records.hrl @@ -0,0 +1,12 @@ +-record(ts_timeline, { + ref, % {username, timelineid} + created,% {{year, month, day}, {hour, minute, second}} + desc +}). + +-record(ts_entry, { + ref, % {username, timelineid, eventid} + timestamp, % {{year, month, day}, {hour, minute, second}} + mark, % String description of entry + notes % String with further notes about the entry +}). diff --git a/src/ts_entry.erl b/src/ts_entry.erl new file mode 100644 index 0000000..c961eea --- /dev/null +++ b/src/ts_entry.erl @@ -0,0 +1,19 @@ +-module(ts_entry). +-export([create_table/1, new/1, update/1, list/3]). + +-include("ts_db_records.hrl"). +-include_lib("stdlib/include/qlc.hrl"). + +create_table(TableOpts) -> + mnesia:create_table(ts_entry, + TableOpts ++ [{attributes, record_info(fields, ts_entry)}, + {type, ordered_set}, {index, timestamp}]). + +new(ER = #ts_entry()) -> + {atmoic, NewRow) = mnesia:transaction(fun() -> + {Username, TimelineId, _} = ER#ts_entry.ref, + NextId = id_counter:next_counter(ts_entry_id), + NewRow = ER#ts_entry{ref = {Username, TimelineId, NextId}}, + ok = mnesia:write(NewRow), + NewRow end), + {ok, NewRow}. diff --git a/src/ts_timeline.erl b/src/ts_timeline.erl new file mode 100644 index 0000000..4807aed --- /dev/null +++ b/src/ts_timeline.erl @@ -0,0 +1,34 @@ +-module(ts_timeline). +-export([create_table/1, new/1, update/1, list/3]). + +-include("ts_db_records.hrl"). +-include_lib("stdlib/include/qlc.hrl"). + +create_table(TableOpts) -> + mnesia:create_table(ts_timeline, + TableOpts ++ [{attributes, record_info(fields, ts_timeline)}, + {type, ordered_set}]). + +new(TR = #ts_timeline{}) -> + case mnesia:dirty_read(ts_timeline, TR#ts_timeline.ref) of + [ExistingRecord] -> {error, {record_exists, ExistingRecord}}; + [] -> mnesia:dirty_write(TR) + end. + +update(TR = #ts_timeline{}) -> + + % look for the existing record + case mnesia:dirty_read(ts_timeline, TR#ts_timeline.ref) of + % record does not exist + [] -> no_record; + % record exists, update + [Record] -> mnesia:dirty_write(TR) + end. + +list(Username, Start, Length) -> + ts_common:list( + qlc:q([T || T <- mnesia:table(ts_timeline), + T#ts_timeline.ref = {Username, _}]), + Start, Length). + + diff --git a/yaws.conf b/yaws.conf new file mode 100644 index 0000000..c9aed63 --- /dev/null +++ b/yaws.conf @@ -0,0 +1,10 @@ +ebin_dir = /home/jdbernard/projects/timestamper/web-app/ebin +#include_dir = /home/jdbernard/projects/timestamper/web-app/src + +runmod = timestamper + + + port = 8000 + listen = /home/jdbernard/projects/timestamper/web-app/www + appmods = timestamper_api +