停止更新

wp写东西,现在看来确实有点蛋疼了,写东西,确实现在有更人性化的地方,不用自己折腾空间了。也老了,不想折腾那破玩意了。

该空间也续费了好几年,这个blog就这样挂着吧。到期之后,就不要了。也或许那天心血来潮,就删除了这个网站,换成其他玩意玩玩了。

这个网站记录了我的稚嫩吧,再见了。

我的简书:http://www.jianshu.com/users/712e863f51d9/latest_articles

我的技术Blog:http://www.roowe.net,托管在github page

写于2017年1月1日

浅谈游戏配置数据在erlang使用-2

前生

一年前,我写了篇浅谈游戏配置数据在erlang使用,一年后,我在同事的提示下,我又简化实现,甚至可以无限扩展了,原先拼字符串会有种种局限性。

原理

本质上,配置数据转代码而已,然后可以将数据用erl_syntax的接口表示成代码的AST,接着用format接口转化成具体的代码。这中间使用到了erlang的AST,理论上,你想生成什么样的erlang代码都可以,毕竟erl_syntax是一个erlang完备的语法树。

example

这里只是实验性代码,具体完备功能就按照各位具体项目需求去各自实现吧,有疑问可以发email找我探讨下。

-module(erl_syntax_try).
-compile(export_all).
test() ->
    RecordName = erl_syntax:atom(base_dungeon),
    erl_syntax:record_expr(RecordName, [
                                        erl_syntax:record_field(erl_syntax:atom(key0), erl_syntax:abstract(1)),
                                        erl_syntax:record_field(erl_syntax:atom(key1), erl_syntax:abstract(atom)),
                                        erl_syntax:record_field(erl_syntax:atom(key2), erl_syntax:abstract("xxx")),
                                        erl_syntax:record_field(erl_syntax:atom(key3), erl_syntax:abstract(<<"hello中文"/utf8>>)),
                                        erl_syntax:record_field(erl_syntax:atom(key3), 
                                                                erl_syntax:binary([erl_syntax:binary_field(
                                                                                     erl_syntax:string("hello中文"),
                                                                                     [erl_syntax:atom(utf8)]
                                                                                    )])
                                                               )
                                      ]).
 
write() ->
    file:write_file("/tmp/1.erl", [unicode:characters_to_binary(erl_prettypr:format(test()))]).
 
format() ->
    io:format("~w~n", [erl_prettypr:format(test())]).

生成的代码长这样

#base_dungeon{key0 = 1, key1 = atom, key2 = "xxx",
                  key3 =
                      <<104, 101, 108, 108, 111, 228, 184, 173, 230, 150,
                        135>>,
                        key3 = <<"hello中文"/utf8>>}

最后,加个函数什么就可以了。

我们项目的生成的代码例子如下

%% version : 002a4bbf4625aa6234ae8c8cdc170417
%% Warning:本文件由data_generate自动生成,请不要手动修改
 
-module(data_base_treasure_award).
 
-export([get/1]).
 
-include("db_base_treasure_award.hrl").
 
get(1) ->
    #base_treasure_award{id = 1, item_id = 20001,
			 award_count = 1};
get(2) ->
    #base_treasure_award{id = 2, item_id = 20002,
			 award_count = 1};
get(3) ->
    #base_treasure_award{id = 3, item_id = 20003,
			 award_count = 1};
get(4) ->
    #base_treasure_award{id = 4, item_id = 20004,
			 award_count = 1};
get(5) ->
    #base_treasure_award{id = 5, item_id = 0,
			 award_count = 1};
get(6) ->
    #base_treasure_award{id = 6, item_id = 3,
			 award_count = 1};
get(7) ->
    #base_treasure_award{id = 7, item_id = 1,
			 award_count = 1};
get(8) ->
    #base_treasure_award{id = 8, item_id = 1,
			 award_count = 2};
get(9) ->
    #base_treasure_award{id = 9, item_id = 1,
			 award_count = 3};
get(10) ->
    #base_treasure_award{id = 10, item_id = 2,
			 award_count = 100};
get(11) ->
    #base_treasure_award{id = 11, item_id = 2,
			 award_count = 500};
get(12) ->
    #base_treasure_award{id = 12, item_id = 2,
			 award_count = 1000};
get(13) ->
    #base_treasure_award{id = 13, item_id = 25009,
			 award_count = 1};
get(Val) -> lager:warning("get not find ~p", [Val]), [].

最后

这仅仅是个data2code的一个思路,欢迎交流。

Erlang简易的MySQL ORM实现

目的

在此之前,我写过一篇,如何在erlang去拼接SQL语句,《在erlang简单使用SQL语句》

现在,在这个基础上,对record做一层封装,我将一个#player{}扔到某个函数,就能帮我写数据库或者,删掉,或者更新等的一些常用几种db操作。

注:本文讨论的是,针对单record,也就是一张表,所以下面,你也不会看到多张表,联表什么的,现在大家只是把数据库做持久化而已,逻辑全部在内存做。也就是说player和goods要搞基,也是在内存,而不是通过SQL语句。

老项目的做法

%% 更新用户卡数据
update_user_card(RecUserCard) ->
    [id | FieldList] = record_info(fields, rec_user_card),
    ValueList = lists:nthtail(2, tuple_to_list(RecUserCard)),
    ZipList = lists:zip(FieldList, ValueList),
    db_center:update(user_card, ZipList, [{id, RecUserCard#rec_user_card.id}]).

上面这段代码比较典型,抽象地讲,update无非就是db table update_clause, where_clause等几个东西,但是我们代码当时各种复制粘帖,有时候对这些代码还是比较烦躁。

这种,相比那些直接将字段写死在代码又稍微好点,数据库将nickname2改为nickname的时候,代码不用动,因为我们做了从数据库映射成record声明的东西,声明变了,也就是record_info变了,再后面也就和数据库对上了。

新的做法

声明一个record_mysql_info,然后新加表的时候,声明下里面的定义。接着传给db_mysql_base就可以,什么zip之类就不用写了。

没有带r的就是针对这个表的操作,带r的就是传record进来。考虑有时候并不是操作record,而是操作该表,所以提供通用的没有record的操作,再特殊点,就直接去调erl_mysql,还不行就能自己拼写SQL(假设多表操作很少用,就不做支持了)。

核心代码也就那么一点点啦。

%% 约定:
%% 1、第一个字段是键值,作为key存在,跟mnesia的规则一样,简化底层代码,提高运行效率,通过约束减少不必要的运算
-module(db_mysql_base).
 
-export([select/2, select/3, update/3, delete/2]).
 
-export([
         r_update/2,
         r_delete/2,
         r_insert/2, 
         r_list_insert_withnot_id/2,
         r_list_insert_with_id/2
        ]).
 
-include_lib("emysql/include/emysql.hrl").
-record(record_mysql_info,{
          db_pool,
          table_name,
          fields,
          record_name,
          mod
         }).
 
 
select(RecordMysqlInfo, WhereClause) -> 
    select(RecordMysqlInfo, WhereClause, undefined).
 
select(#record_mysql_info{
          db_pool = DbPool,
          table_name = TableName,
          record_name = RecordName,
          mod = ModelMod
         }, WhereClause, Extras) ->
    SQL = iolist_to_binary(erl_mysql:select('*', TableName, WhereClause, Extras)),
    run_rows(DbPool, SQL,
             fun(List) ->
                     [ModelMod:out_db_hook(list_to_tuple([RecordName|Vals])) || Vals <- List]
             end).
 
update(#record_mysql_info{
          db_pool = DbPool,
          table_name = TableName
         }, UpdateClause, WhereClause) ->
    SQL = iolist_to_binary(erl_mysql:update(TableName, UpdateClause, WhereClause)),
    run_affected(DbPool, SQL).
 
delete(#record_mysql_info{
          db_pool = DbPool,
          table_name = TableName
         }, WhereClause) ->
    SQL = iolist_to_binary(erl_mysql:delete(TableName, WhereClause)),
    run_affected(DbPool, SQL).
 
%% 下面带r的是,是针对record的接口
%% 返回值{ok, Record},会自动处理,需不需要加上insert_id
%% 或者{error, Result}
r_insert(#record_mysql_info{
            db_pool = DbPool,
            table_name = TableName,
            fields = [_|RestFields] = Fields,
            mod = ModelMod
         }, Record) when is_tuple(Record)->
    [UndefId|RestVals] = Vals = record_to_vals(ModelMod, Record),
    {FilterUndefIdFields, FilterUndefIdVals} = 
        if
            UndefId =:= undefined ->
                {RestFields, RestVals};
            true ->
                {Fields, Vals}
        end,    
    SQL = iolist_to_binary(erl_mysql:insert(TableName, {FilterUndefIdFields, [FilterUndefIdVals]})),
    case emysql:execute(DbPool, SQL) of
        Result when is_record(Result, ok_packet) ->
            if 
                UndefId =:= undefined ->
                    {ok, setelement(2, Record, emysql_util:insert_id(Result))};
                true ->
                    {ok, Record}
            end;
        Result when is_record(Result, error_packet) ->
            {error, Result}
    end.
 
%% 多行插入的混合接口的返回值实在不好做,干脆要么有id要么没有id,
%% 在表设计的时候已经决定了这点,正常设计不会出现有时候要向MySQL要id,有时候又不要。
%% 多行插入也就日志系统使用会有一定的优化,其他场合应该都一条一条插入
r_list_insert_withnot_id(#record_mysql_info{
                            db_pool = DbPool,
                            table_name = TableName,
                            fields = [_|RestFields],
                            mod = ModelMod
                           }, RecordList) 
  when is_list(RecordList) ->    
    FilterUndefIdValsList = [tl(record_to_vals(ModelMod, Record)) || Record <- RecordList],
    SQL = iolist_to_binary(erl_mysql:insert(TableName, {RestFields, FilterUndefIdValsList})),    
    run_affected(DbPool, SQL).
 
r_list_insert_with_id(#record_mysql_info{
                         db_pool = DbPool,
                         table_name = TableName,
                         fields = Fields,
                         mod = ModelMod
                        }, RecordList) 
  when is_list(RecordList) ->
    ValsList = [record_to_vals(ModelMod, Record) || Record <- RecordList],
    SQL = iolist_to_binary(erl_mysql:insert(TableName, {Fields, ValsList})),    
    run_affected(DbPool, SQL).
 
 
%% update list的返回值不好定,所以不支持
r_update(#record_mysql_info{
            db_pool = DbPool,
            table_name = TableName,
            fields = [IdField|RestFields],
            mod = ModelMod
           }, Record) ->
    [Id|RestVals] = record_to_vals(ModelMod, Record),
    WhereClause = {IdField, '=', Id},
    UpdateClause = lists:zip(RestFields, RestVals),
    SQL = iolist_to_binary(erl_mysql:update(TableName, UpdateClause, WhereClause)),
    run_affected(DbPool, SQL).
 
 
r_delete(#record_mysql_info{
            db_pool = DbPool,
            table_name = TableName,
            fields = [IdField|_]
           }, Record) 
  when is_tuple(Record) ->
    Id = element(2, Record),
    WhereClause = {IdField, '=', Id},
    SQL = iolist_to_binary(erl_mysql:delete(TableName, WhereClause)),
    run_affected(DbPool, SQL);
r_delete(#record_mysql_info{
            db_pool = DbPool,
            table_name = TableName,
            fields = [IdField|_]
           }, RecordList) 
  when is_list(RecordList) ->
    Ids = [element(2, Record) || Record <- RecordList],
    WhereClause = {IdField, 'in', Ids},
    SQL = iolist_to_binary(erl_mysql:delete(TableName, WhereClause)),
    run_affected(DbPool, SQL).
 
 
%% -------------------- emysql封装 --------------------
run_affected(DbPool, SQL) ->
    case emysql:execute(DbPool, SQL) of
        Result when is_record(Result, ok_packet) ->
            {ok, emysql_util:affected_rows(Result)};
        Result when is_record(Result, error_packet) ->
            {error, Result}
    end.
 
%% run_rows(DbPool, SQL) ->
%%     run_rows(DbPool, SQL, fun(A) -> A end).
 
run_rows(DbPool, SQL, ResultFun) ->
    case emysql:execute(DbPool, SQL) of
        #result_packet{
           rows = Result
          } ->
            {ok, ResultFun(Result)};
        Result when is_record(Result, error_packet) ->
            {error, Result}
    end.
 
%% in_db_hook 存入数据库之前的操作
record_to_vals(ModelMod, Record) 
  when is_tuple(Record)->
    tl(tuple_to_list(ModelMod:in_db_hook(Record)));
record_to_vals(ModelMod, RecordList) 
  when is_list(RecordList)->
    [tl(tuple_to_list(ModelMod:in_db_hook(Record))) || Record <- RecordList].

使用例子

-module(db_player).
 
-export([select_by_id/1]).
 
-export([in_db_hook/1, out_db_hook/1]).
 
-include("db_player.hrl").
-include("define_mysql.hrl").
 
-define(TABLE_CONF, #record_mysql_info{
                       db_pool = db_game,
                       table_name = player,
                       record_name = player,
                       mod = ?MODULE,
                       fields = record_info(fields, player)
                      }).
 
 
%% --------------------通用代码--------------------
-export([update/1, delete/1, insert/1, r_list_insert_withnot_id/1, r_list_insert_with_id/1]).
 
update(Record)->
    db_mysql_base:r_update(?TABLE_CONF, Record).
 
delete(RecordOrList) ->
    db_mysql_base:r_delete(?TABLE_CONF, RecordOrList).
 
insert(Record) ->
    db_mysql_base:r_insert(?TABLE_CONF, Record).
 
r_list_insert_withnot_id(List) ->
    db_mysql_base:r_list_insert_withnot_id(?TABLE_CONF, List).
 
r_list_insert_with_id(List) ->
    db_mysql_base:r_list_insert_with_id(?TABLE_CONF, List).
 
in_db_hook(Record) ->
    Record.
 
out_db_hook(Record) ->
    Record
 
%% ----------------------------------------
 
select_by_id(Id) ->
    db_mysql_base:select(?TABLE_CONF, {id, '=', Id}).

有了db_player,我们就传#player{}就可以了,再也不用关心数据库字段啥,拼SQL啥,哪个表什么的等等,配好定义,一劳永逸。

在Linux下用wget批量下载迅雷离线的东西

标题略长了。

在Linux下资源下载的速度长期受限,ed2k,torrent什么都木有速度,坑爹呀,自从购买了迅雷VIP的服务,可以直接以http形式来从迅雷服务器内下载自己托迅雷下载的东西,好别扭的描述呐,而且如果你这个资源别人下载过的话,你就不用再次下载了,迅雷马上提示你这个任务已经完成了。至于其他的,用过的人都知道了,也不再细说。如果windows平台配合迅雷客户端用迅雷VIP的话,这个脚本也没有啥意义了(因为客户端更人性化^_^,当然占用资源也不少,嘿嘿),所以前提是你的OS要是Linux,然后使用迅雷离线的web界面。 Continue reading