Skip to content

Commit 093d9f0

Browse files
author
Kevin Montuori
committed
::uuid() is now a valid Id type
1 parent 22cbbd2 commit 093d9f0

File tree

5 files changed

+121
-44
lines changed

5 files changed

+121
-44
lines changed

src/boss_db.erl

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -266,36 +266,40 @@ validate_record(Record) ->
266266
validate_record_types(Record) ->
267267
Errors = lists:foldl(fun
268268
({Attr, Type}, Acc) ->
269-
Data = Record:Attr(),
270-
GreatSuccess = case {Data, Type} of
271-
{undefined, _} ->
272-
true;
273-
{Data, string} when is_list(Data) ->
274-
true;
275-
{Data, binary} when is_binary(Data) ->
276-
true;
277-
{{{D1, D2, D3}, {T1, T2, T3}}, datetime} when is_integer(D1), is_integer(D2), is_integer(D3),
278-
is_integer(T1), is_integer(T2), is_integer(T3) ->
279-
true;
280-
{Data, integer} when is_integer(Data) ->
281-
true;
282-
{Data, float} when is_float(Data) ->
283-
true;
284-
{Data, boolean} when is_boolean(Data) ->
285-
true;
286-
{{N1, N2, N3}, timestamp} when is_integer(N1), is_integer(N2), is_integer(N3) ->
287-
true;
288-
{Data, atom} when is_atom(Data) ->
289-
true;
290-
{_Data, Type} ->
291-
false
292-
end,
293-
if
294-
GreatSuccess ->
295-
Acc;
296-
true ->
297-
[lists:concat(["Invalid data type for ", Attr])|Acc]
298-
end
269+
case Attr of
270+
id -> Acc;
271+
_ ->
272+
Data = Record:Attr(),
273+
GreatSuccess = case {Data, Type} of
274+
{undefined, _} ->
275+
true;
276+
{Data, string} when is_list(Data) ->
277+
true;
278+
{Data, binary} when is_binary(Data) ->
279+
true;
280+
{{{D1, D2, D3}, {T1, T2, T3}}, datetime} when is_integer(D1), is_integer(D2), is_integer(D3),
281+
is_integer(T1), is_integer(T2), is_integer(T3) ->
282+
true;
283+
{Data, integer} when is_integer(Data) ->
284+
true;
285+
{Data, float} when is_float(Data) ->
286+
true;
287+
{Data, boolean} when is_boolean(Data) ->
288+
true;
289+
{{N1, N2, N3}, timestamp} when is_integer(N1), is_integer(N2), is_integer(N3) ->
290+
true;
291+
{Data, atom} when is_atom(Data) ->
292+
true;
293+
{_Data, Type} ->
294+
false
295+
end,
296+
if
297+
GreatSuccess ->
298+
Acc;
299+
true ->
300+
[lists:concat(["Invalid data type for ", Attr])|Acc]
301+
end
302+
end
299303
end, [], Record:attribute_types()),
300304
case Errors of
301305
[] -> ok;

src/boss_db_mock_controller.erl

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,21 @@ handle_call({save_record, Record}, _From, [{Dict, IdCounter}|OldState]) ->
4848
Type = element(1, Record),
4949
TypeString = atom_to_list(Type),
5050
{Id, IdCounter1} = case Record:id() of
51-
id -> {lists:concat([Type, "-", IdCounter]), IdCounter + 1};
51+
id -> case keytype(Record) of
52+
uuid -> {lists:concat([Type, "-", uuid:to_string(uuid:v4())]), IdCounter};
53+
_ -> {lists:concat([Type, "-", IdCounter]), IdCounter + 1}
54+
end;
5255
ExistingId ->
53-
[TypeString, IdNum] = string:tokens(ExistingId, "-"),
54-
Max = case list_to_integer(IdNum) of
55-
N when N > IdCounter -> N;
56-
_ -> IdCounter
57-
end,
58-
{lists:concat([Type, "-", IdNum]), Max + 1}
56+
case keytype(Record) of
57+
uuid -> {ExistingId, IdCounter};
58+
_ ->
59+
[TypeString, IdNum] = string:tokens(ExistingId, "-"),
60+
Max = case list_to_integer(IdNum) of
61+
N when N > IdCounter -> N;
62+
_ -> IdCounter
63+
end,
64+
{lists:concat([Type, "-", IdNum]), Max + 1}
65+
end
5966
end,
6067
NewAttributes = lists:map(fun
6168
({id, _}) ->
@@ -88,6 +95,10 @@ code_change(_OldVsn, State, _Extra) ->
8895
handle_info(_Info, State) ->
8996
{noreply, State}.
9097

98+
99+
keytype(Record) ->
100+
proplists:get_value(id, Record:attribute_types(), unspecified).
101+
91102
do_find(Dict, Type, Conditions, Max, Skip, SortBy, SortOrder) ->
92103
Tail = lists:nthtail(Skip,
93104
lists:sort(fun(RecordA, RecordB) ->

src/boss_news_controller.erl

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ handle_call({set_watch, WatchId, TopicString, CallBack, UserInfo, TTL}, From, St
5454
(SingleTopic, {ok, StateAcc, WatchListAcc}) ->
5555
case re:split(SingleTopic, "\\.", [{return, list}]) of
5656
[Id, Attr] ->
57-
[Module, IdNum] = re:split(Id, "-", [{return, list}]),
57+
[Module, IdNum] = re:split(Id, "-", [{return, list}, {parts, 2}]),
5858
{NewState1, WatchInfo} = case IdNum of
5959
"*" ->
6060
SetAttrWatchers = case dict:find(Module, StateAcc#state.set_attr_watchers) of
@@ -75,7 +75,7 @@ handle_call({set_watch, WatchId, TopicString, CallBack, UserInfo, TTL}, From, St
7575
end,
7676
{ok, NewState1, [WatchInfo|WatchListAcc]};
7777
_ ->
78-
case re:split(SingleTopic, "-", [{return, list}]) of
78+
case re:split(SingleTopic, "-", [{return, list}, {parts, 2}]) of
7979
[_Module, _IdNum] ->
8080
IdWatchers = case dict:find(SingleTopic, State#state.id_watchers) of
8181
{ok, Val} -> Val;
@@ -96,7 +96,7 @@ handle_call({set_watch, WatchId, TopicString, CallBack, UserInfo, TTL}, From, St
9696
end;
9797
(_, Error) ->
9898
Error
99-
end, {ok, State, []}, re:split(TopicString, ", +", [{return, list}])),
99+
end, {ok, State, []}, re:split(TopicString, ", +", [{return, list}, {parts, 2}])),
100100
case RetVal of
101101
ok -> {reply, RetVal, NewState#state{
102102
watch_dict = dict:store(WatchId,
@@ -133,7 +133,7 @@ handle_call({extend_watch, WatchId}, _From, State0) ->
133133
{reply, RetVal, NewState};
134134
handle_call({created, Id, Attrs}, _From, State0) ->
135135
State = prune_expired_entries(State0),
136-
[Module | _IdNum] = re:split(Id, "-", [{return, list}]),
136+
[Module | _IdNum] = re:split(Id, "-", [{return, list}, {parts, 2}]),
137137
PluralModel = inflector:pluralize(Module),
138138
{RetVal, State1} = case dict:find(PluralModel, State#state.set_watchers) of
139139
{ok, SetWatchers} ->
@@ -156,7 +156,7 @@ handle_call({created, Id, Attrs}, _From, State0) ->
156156
{reply, RetVal, State1};
157157
handle_call({deleted, Id, OldAttrs}, _From, State0) ->
158158
State = prune_expired_entries(State0),
159-
[Module | _IdNum] = re:split(Id, "-", [{return, list}]),
159+
[Module | _IdNum] = re:split(Id, "-", [{return, list}, {parts, 2}]),
160160
PluralModel = inflector:pluralize(Module),
161161
{RetVal, State1} = case dict:find(PluralModel, State#state.set_watchers) of
162162
{ok, SetWatchers} ->
@@ -182,7 +182,7 @@ handle_call({deleted, Id, OldAttrs}, _From, State0) ->
182182
{reply, RetVal, State1};
183183
handle_call({updated, Id, OldAttrs, NewAttrs}, _From, State0) ->
184184
State = prune_expired_entries(State0),
185-
[Module | _IdNum] = re:split(Id, "-", [{return, list}]),
185+
[Module | _IdNum] = re:split(Id, "-", [{return, list}, {parts, 2}]),
186186
IdWatchers = case dict:find(Id, State#state.id_attr_watchers) of
187187
{ok, Val} -> Val;
188188
_ -> []
@@ -242,7 +242,7 @@ future_time(TTL) ->
242242
MegaSecs * 1000 * 1000 + Secs + TTL.
243243

244244
activate_record(Id, Attrs) ->
245-
[Module | _IdNum] = re:split(Id, "-", [{return, list}]),
245+
[Module | _IdNum] = re:split(Id, "-", [{return, list}, {parts, 2}]),
246246
Type = list_to_atom(Module),
247247
DummyRecord = boss_record_lib:dummy_record(Type),
248248
apply(Type, new, lists:map(fun

src/boss_record_compiler.erl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ process_tokens([{']',_},{')',_},{dot,_}|_]=Tokens, TokenAcc, Acc) ->
3636
process_tokens([{'-',N}=T1,{atom,N,module}=T2,{'(',_}=T3,{atom,_,_ModuleName}=T4,{',',_}=T5,
3737
{'[',_}=T6,{var,_,'Id'}=T7|Rest], TokenAcc, []) ->
3838
process_tokens(Rest, lists:reverse([T1, T2, T3, T4, T5, T6, T7], TokenAcc), []);
39+
process_tokens([{'-',_N}=T1,{atom,_,module}=T2,{'(',_}=T3,{atom,_,_ModuleName}=T4,{',',_}=T5,
40+
{'[',_}=T6,{var,_,'Id'}=T7,{'::',_},{atom,_,VarType},{'(',_},{')',_}|Rest], TokenAcc, []) ->
41+
process_tokens(Rest, lists:reverse([T1, T2, T3, T4, T5, T6, T7], TokenAcc), [{'Id', VarType}]);
3942
process_tokens([{',',_}=T1,{var,_,VarName}=T2,{'::',_},{atom,_,VarType},{'(',_},{')',_}|Rest], TokenAcc, Acc) ->
4043
process_tokens(Rest, lists:reverse([T1, T2], TokenAcc), [{VarName, VarType}|Acc]);
4144
process_tokens([H|T], TokenAcc, Acc) ->

src/lib/uuid.erl

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
% Copyright (c) 2008, Travis Vachon
2+
% All rights reserved.
3+
%
4+
% Redistribution and use in source and binary forms, with or without
5+
% modification, are permitted provided that the following conditions are
6+
% met:
7+
%
8+
% * Redistributions of source code must retain the above copyright
9+
% notice, this list of conditions and the following disclaimer.
10+
%
11+
% * Redistributions in binary form must reproduce the above copyright
12+
% notice, this list of conditions and the following disclaimer in the
13+
% documentation and/or other materials provided with the distribution.
14+
%
15+
% * Neither the name of the author nor the names of its contributors
16+
% may be used to endorse or promote products derived from this
17+
% software without specific prior written permission.
18+
%
19+
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20+
% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21+
% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22+
% A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23+
% OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24+
% SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
25+
% TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26+
% PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27+
% LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28+
% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29+
% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
%
31+
-module(uuid).
32+
-export([v4/0, to_string/1, get_parts/1, to_binary/1]).
33+
-import(random).
34+
35+
% Generates a random binary UUID.
36+
v4() ->
37+
v4(random:uniform(round(math:pow(2, 48))) - 1, random:uniform(round(math:pow(2, 12))) - 1, random:uniform(round(math:pow(2, 32))) - 1, random:uniform(round(math:pow(2, 30))) - 1).
38+
v4(R1, R2, R3, R4) ->
39+
<<R1:48, 4:4, R2:12, 2:2, R3:32, R4: 30>>.
40+
41+
% Returns a string representation of a binary UUID.
42+
to_string(U) ->
43+
lists:flatten(io_lib:format("~8.16.0b-~4.16.0b-~4.16.0b-~2.16.0b~2.16.0b-~12.16.0b", get_parts(U))).
44+
45+
% Returns the 32, 16, 16, 8, 8, 48 parts of a binary UUID.
46+
get_parts(<<TL:32, TM:16, THV:16, CSR:8, CSL:8, N:48>>) ->
47+
[TL, TM, THV, CSR, CSL, N].
48+
49+
% Converts a UUID string in the format of 550e8400-e29b-41d4-a716-446655440000
50+
% (with or without the dashes) to binary.
51+
to_binary(U)->
52+
convert(lists:filter(fun(Elem) -> Elem /= $- end, U), []).
53+
54+
% Converts a list of pairs of hex characters (00-ff) to bytes.
55+
convert([], Acc)->
56+
list_to_binary(lists:reverse(Acc));
57+
convert([X, Y | Tail], Acc)->
58+
{ok, [Byte], _} = io_lib:fread("~16u", [X, Y]),
59+
convert(Tail, [Byte | Acc]).

0 commit comments

Comments
 (0)