这个不是什么高深的内容,记录下来,给自己提个醒。
起源于我想给我的所有数据库添加一个统一的操作接口,当然,使用英
文会更方便,更批量化,代码更少等等,我在弄的时候突发奇想,使用中
文吧,这样网页中看着连想都不用想,而且还可以给别人看,连教育部都
不支持中文了,杂家也随下大流。
其实erlang的record完全支持中文的声明,比如下面的例子:
-record('公告',
{'ID', %ID
'标题' = "", % 标题
'内容' = "", % 描述
'显示' = true% 禁用
}).
只所以可以成功是因为单引号的字符串,在erlang中就是作为一个atom
类型的,如果你想给里面加个空格什么的,也没问题。当然,使用的时候也
要加上单引号。
其实,erlang的record使用中文的话,也就上面的这些。但如果在shell
中使用,就比较烦人了,在命令行下使用下面的语句:
mnesia:transaction(fun() -> mnesia:match_object('公告') end)).
会出现:illegal atom 的错误,或许修改环境变量的编码可以修改,我
没有测试。
接下来是http的REST接口,一般都使用的是表格显示,我喜欢使用JSON数
据格式,这个就更简单些了。还是上面的record:
info(Db) ->
Record = mnesia:table_info(Db, attributes),
[
{[{"name", list_to_binary(atom_to_list(X))}, {"field", list_to_binary(atom_to_list(X))}],
X
} || X<- Record
].
db_get_layout(Db) ->
[X || {X, _} <- info(Db)].
上面的代码是中英文通用的,如果传入的Db是中文的话,这个必须首先创
建了数据库才可以调用,这里可能有一个问题:
为什么不用atom_to_binary函数?
我也想用啊,但使用atom_to_binary(X, utf8)在utf8编码下,客户端显
示出来是乱码,当然,一切的文件和环境都是utf8编码,因此,不存在什么
编码不统一的问题,我是使用dojo来显示的。总之,上面的解决了我的问题。
上面的代码仅生成一个list,如何生成json的结构呢,很简单:
mochijson2:encode(db_get_layout('公告')).
在info函数中,生成的每一项是一个长度为2的tuple,第二个元素是在获
取的时候做匹配用的:
put_record([], _Db, _, _Data, Result) ->
lists:reverse(Result);
put_record([Record|Rest], Db, N, Data, Result) ->
put_record(Rest, Db, N+1, Data, [data_to_json(Db, Record, element(N, Data)) | Result]).
db_get(Db) ->
case db:find(Db) of
{atomic, []} ->
[];
{atomic, List} ->
Info = [X || {_, X} <- info(Db)],
F = fun(One) ->
put_record(Info, Db, 2, One, [])
end,
[F(X) || X<- List]
end.
这里有一个data_to_json的函数,只所以将每一项都调用这个函数处理是
因为数据可能要自定义格式来显示,虽然可以使用通用的io_lib:format函数
来统一实现,但谁也不希望一个字符串使用列表的方式表现出来或者显示乱
码吧。因此,put_record就可以这样写了:
data_to_json(_Db, 'ID', Data) ->
{<<"ID">>, Data};
data_to_json('公告', '标题', Data) ->
{<<"标题">>, list_to_binary(Data)};
data_to_json('公告', '显示', Data) ->
{<<"显示">>, Data};
data_to_json('公告', '内容', Data) ->
{<<"内容">>, list_to_binary(Data)};
data_to_json(_, P, Data) ->
{list_to_binary(atom_to_list(P)), Data}.
我们假设所有的ID都是统一的,一般也都是统一的,所以可以忽略掉Db的
参数了,其他的带上Db的参数的话,在匹配时只针对这个Db的指定数据段。
将生成的数据转换为json不用提了,之前就有。接下来说数据的更新或添
加,我希望的类似上传格式是这样子的:
[{
"ID": 15,
"标题": "中风的萨芬撒",
"内容": "fdsafs中文范德萨减肥的扫过地横扫",
"显示": true
},
{
"ID": 16,
"标题": "中风的萨芬撒",
"内容": "fdsafs中文范德萨减肥的扫过地横扫",
"显示": true
}
]
上面的文件内容要求是UTF-8编码,每个字段的字段名就是表头的名字,也
就是数据中中的每一项的名字。为了统一处理,我在接受到的时候,做了一下
总体的封装:
loop(Req, DocRoot) ->
case Req:get(method) ->
'PUT' ->
case Path of
"db/" ++ Db ->
Db = list_to_existing_atom(Db),
case [X || X<- mnesia:system_info(tables),
X =:= Db] of
[] ->
Req:ok({"text/plain", "not found db"});
[Db] ->
PostData = Req:recv_body(),
db_put(Db, mochijson2:decode(PostData, [{format, proplist}])),
Req:ok({"text/plain", "db update"});
[_] ->
Req:ok({"text/plain", "db not support"})
end;
_ ->
Req:ok({"text/plain", "not support"})
end;
......
首先会将JSON格式统一转换为proplist所支持的格式,然后传递给db_put
函数,db_put函数及其他内容如下:
write('公告', Data) ->
Ret = case proplists:get_value('ID', Data, 0) of
0 ->
#'公告'{
'ID' = counter:bump('公告'),
'标题' = proplists:get_value('标题', Data, ""),
'内容' = proplists:get_value('内容', Data, ""),
'显示' = proplists:get_value('显示', Data, true)
};
Id ->
case db:find('公告', Id) of
{atomic, []} ->
#'公告' {
'ID' = Id,
'标题' = proplists:get_value('标题', Data, ""),
'内容' = proplists:get_value('内容', Data, ""),
'显示' = proplists:get_value('显示', Data, true)
};
{atomic, [T]} ->
T#'公告'{
'标题' = proplists:get_value('标题', Data, T#'公告'.'标题'),
'内容' = proplists:get_value('内容', Data, T#'公告'.'内容'),
'显示' = proplists:get_value('显示', Data, T#'公告'.'显示')
}
end
end,
{ok, Ret};
write(_A, Data) ->
{error, Data}.
db_put(Db, []) ->
ok;
db_put(Db, [A|Rest]) ->
case check(Db, A, {ok, []}) of
{ok, Data} ->
case write(Db, Data) of
{ok, Write} ->
mnesia:transaction(fun() -> mnesia:write(Write) end);
{error, Data} ->
io:format("Error~p~n", [Data])
end;
Error ->
io:format("Error=~p~n", [Error])
end,
db_put(Db, Rest).
check(_Db, [], Result) ->
Result;
check(Db, [A|Rest], {Err, Result}) ->
case check(Db, A) of
{ok, Data} ->
check(Db, Rest, {ok, [Data|Result]});
{error, D} ->
{error, D}
end.
check('公告', {<<"显示">>, Data}) ->
case Data of
true ->
{ok, {'显示', true}};
false ->
{ok, {'显示', false}};
_ ->
{error, Data}
end;
check('公告', {<<"标题">>, Data}) ->
case is_binary(Data) and (byte_size(Data)> 0) of
true ->
{ok, {'标题', binary_to_list(Data)}};
_ ->
{error, Data}
end;
check('公告', {<<"内容">>, Data}) ->
case is_binary(Data) and (byte_size(Data)> 0) of
true ->
{ok, {'内容', binary_to_list(Data)}};
_ ->
{error, Data}
end;
check(_Db, {<<"ID">>, Id}) ->
case is_integer(Id) and (Id > 0) of
true ->
{ok, {'ID', Id}};
_ ->
{error, Id}
end.
因为每个数据库的每个字段需要的检查内容可能都不一样, 但也可能一样,
使用上面的方式,可以灵活定制下。所有的内容项检查完毕后,交由write函数
进行封装,write函数会针对每个数据库的需要进行最后的检查,检查通过之后,
生成对应的数据格式, 如果一切都无误的话,写入到数据库。
REST数据可以使用curl方便进行操作,比如:
curl -T test.txt -X PUT http://127.0.0.1:8080/db/公告
以上内容所有文件均在UTF-8编码下测试完成。
总体而言,还是英文好些,至少不用中英文一直切换那个烦啊。对于本地语
言的定制,或许做一个翻译对应的ets是比较好的方式了。