Bug fixes and implementation refinement around `ts_ext_data`.

* Transformed the test database to match the new data model. Added
  ``ts_ext_data`` table and moved ``ts_user.ext_data`` values to it.
* Added D0022: cascade delete ``ts_ext_data`` when ``ts_entry`` record is
  deleted.
* Completed refactor of ``ts_api`` functions to account for extended data:

    * ``post_entry/3``
    * ``put_entry/3``

* Created ``ts_entry:write/2`` to write extended data atomically with the entry.
* Fixed copy/paste bug in ``ts_ext_data:create_table/1``.
* Fixed implementation of ``ts_ext_data:set_property/3`` to be explicit about
  taking a record, not a reference. It then extracts the reference and passes it
  to the underlying implementation in ``ts_ext_data:do_set_property/3``.
* Refactored ``ts_ext_data:do_set_property/3`` to take a record *reference*, not
  the record itself.
* Refactored the ``ts_ext_data:get_property/2`` and
  ``ts_ext_data:get_properties/1`` functions to return key value tuple pairs,
  not ``ts_ext_data{}`` records.
This commit is contained in:
Jonathan Bernard
2011-06-15 07:34:41 -05:00
parent 658091e947
commit 3999e736be
10 changed files with 38 additions and 19 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+10
View File
@@ -0,0 +1,10 @@
Deleting an entry should cascade delete extended data.
======================================================
Currently the data remains in the database. It should not cause any
problems, but it is wasting space.
========= ==========
Created: 2011-06-15
Resolved: YYYY-MM-DD
========= ==========
+4 -5
View File
@@ -418,11 +418,10 @@ post_entry(YArg, Username, TimelineId) ->
#ts_entry{ref = {Username, TimelineId, undefined}}, EJSON)
catch _:InputError ->
error_logger:error_report("Bad input: ~p", [InputError]),
throw(make_json_400(YArg))
throw(make_json_400(YArg, {request_error, InputError}))
end,
%% TODO; should entries and their properties be created atomically?
case ts_entry:new(ER) of
case ts_entry:new(ER, ExtData) of
% record created
{ok, CreatedRecord} ->
@@ -445,14 +444,14 @@ put_entry(YArg, Username, TimelineId, EntryId) ->
EJSON = parse_json_body(YArg),
% parse into ts_entry record
ER = try ts_json:ejson_to_record_strict(
{ER, ExtData} = try ts_json:ejson_to_record_strict(
#ts_entry{ref={Username, TimelineId, EntryId}}, EJSON)
catch _:InputError ->
error_logger:error_report("Bad input: ~p", [InputError]),
throw(make_json_400(YArg))
end,
ts_entry:write(ER),
ts_entry:write(ER, ExtData),
make_json_200(YArg, ER).
delete_entry(YArg, Username, TimelineId, EntryId) ->
+9 -2
View File
@@ -1,6 +1,6 @@
-module(ts_entry).
-export([create_table/1, new/1, new/2, update/1, update/2, write/1, delete/1,
lookup/3, list_asc/3, list_desc/3]).
-export([create_table/1, new/1, new/2, update/1, update/2, write/1, write/2,
delete/1, lookup/3, list_asc/3, list_desc/3]).
-include("ts_db_records.hrl").
-include_lib("stdlib/include/qlc.hrl").
@@ -35,6 +35,13 @@ update(ER = #ts_entry{}, ExtData) when is_list(ExtData) ->
write(ER = #ts_entry{}) -> mnesia:dirty_write(ER).
write(ER = #ts_entry{}, ExtData) ->
{atomic, Result} = mnesia:transcation(fun() ->
ok = mnesia:write(ER),
ok = ts_common:do_set_ext_data(ER, ExtData)
end),
Result.
lookup(Username, TimelineId, EntryId) ->
case mnesia:dirty_read(ts_entry, {Username, TimelineId, EntryId}) of
[] -> no_record;
+15 -12
View File
@@ -4,41 +4,44 @@
-include("ts_db_records.hrl").
create_table(TableOpts) ->
mnesia:create_table(ts_entry,
mnesia:create_table(ts_ext_data,
TableOpts ++ [{attributes, record_info(fields, ts_ext_data)},
{type, ordered_set}]).
% set last timeline
set_property(Ref=#ts_user{}, last_timeline, LastTimelineId) ->
do_set_property(Ref, last_timeline, LastTimelineId);
set_property(Rec=#ts_user{}, last_timeline, LastTimelineId) ->
do_set_property(Rec#ts_user.username, last_timeline, LastTimelineId);
% Set exclusion_list for a User account
set_property(Ref=#ts_user{}, entry_exclusions, ExclusionList) ->
do_set_property(Ref, entry_exclusions, string:join(ExclusionList, "|"));
set_property(Rec=#ts_user{}, entry_exclusions, ExclusionList) ->
do_set_property(Rec#ts_user.username, entry_exclusions, ExclusionList);
% Set exclusion_list for a Timeline entry
set_property(Ref=#ts_timeline{}, entry_exclusions, ExclusionList) ->
do_set_property(Ref, entry_exclusions, string:join(ExclusionList, "|"));
set_property(Rec=#ts_timeline{}, entry_exclusions, ExclusionList) ->
do_set_property(Rec#ts_timeline.ref, entry_exclusions, ExclusionList);
set_property(Ref, Key, Value) ->
set_property(Rec, Key, _Value) ->
throw(io_lib:format("Property '~s' not available for a ~s record.",
[Key, element(1, Ref)])).
[Key, element(1, Rec)])).
get_property(Ref, PropKey) ->
{atomic, Result} = mnesia:transaction(fun() ->
case mnesia:read(ts_ext_data, {Ref, PropKey}) of
[] -> not_set;
[Property] -> Property
[Property] -> Property#ts_ext_data.value
end
end),
Result.
get_properties(Ref) ->
get_properties(Rec) ->
Ref = element(2, Rec),
{atomic, Result} = mnesia:transaction(fun() ->
MatchHead = #ts_ext_data{ref = {Ref, '_'}, _='_'},
mnesia:select(ts_ext_data, [{MatchHead, [], ['$_']}])
end),
Result.
lists:map(fun(ExtData = #ts_ext_data{}) ->
{Ref, Key} = ExtData#ts_ext_data.ref,
{Key, ExtData#ts_ext_data.value} end, Result).
do_set_property(Ref, PropKey, Val) ->
{atomic, Result} = mnesia:transaction(fun() ->