From Rails to Erlyweb - Part III

本文揭示了Erlyweb框架背后的元编程魔法,通过动态类型、热代码交换等特性,展示了如何自动生成CRUD代码,并提供了分解编译后的Erlang模块的方法,使读者能够深入了解这些自动生成的功能是如何工作的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3. The Magic Behind Erlyweb

With dynamic typing, hot code swapping, built-in parsing and compilation tools, Erlang is also suitable for dynamic meta-programming. Erlyweb uses a small convenient tool smerl to generate db record CRUD code automatically.

The music example on erlyweb.org shows the magic:

Define a simple musician.erl module (just one line here):

-module(musician).

Then, you will get a long list functions for musician module after erlyweb:compile("apps/music"). Sounds similar to Rails.

I like to watch magic show, but I can't stand that I do not know the things behind magic in programming. For Rails, the magic behind it always makes me headache, it's too difficult to follow who, where, some code are injected into a class or module. But in Erlang, it's super easy.

I add a simple function to erlyweb_app.erl (please see my previous post):

-export([decompile/2]).

decompile(AppName, Beam) when is_atom(AppName) ->
    decompile(atom_to_list(AppName), Beam);
decompile(AppName, Beam) when is_list(AppName) ->
    BinFilename = "./apps/" ++ AppName ++ "/ebin/" ++ atom_to_list(Beam),
    io:format("Beam file: ~s~n", [BinFilename]),
    {ok, {_, [{abstract_code, {_, AC}}]}} = beam_lib:chunks(BinFilename, [abstract_code]),
    SrcFilename = "./apps/" ++ AppName ++ "_" ++ atom_to_list(Beam),
    {ok, S} = file:open(SrcFilename ++ ".erl", write),
    io:fwrite(S, "~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]). 

Now type erlyweb_decompile(music, musician) under the Erlang shell, I get a file: music_musician.erl under folder myproject/apps/ (I put these decompiled source files under /myproject/apps/ to avoid that they are auto-compiled to beams by erlyweb again ):

-file("musician", 1).

-module(musician).

-export([relations/0, fields/0, table/0, type_field/0,
   db_table/0, db_field/1, is_new/1, is_new/2,
   get_module/1, to_iolist/1, to_iolist/2,
   field_to_iolist/2, new/0, new_with/1, new_with/2,
   new_from_strings/1, set_fields/2, set_fields/3,
   set_fields_from_strs/2, field_from_string/2, save/1,
   insert/1, update/1, update/2, delete/1, delete_id/1,
   delete_where/1, delete_all/0, transaction/1,
   before_save/1, after_save/1, before_delete/1,
   after_delete/1, after_fetch/1, find/2, find_first/2,
   find_max/3, find_range/4, find_id/1, aggregate/4,
   count/0, get/2, set_related_one_to_many/2,
   find_related_one_to_many/2, find_related_many_to_one/4,
   aggregate_related_many_to_one/6,
   add_related_many_to_many/3,
   remove_related_many_to_many/3,
   remove_related_many_to_many_all/5,
   find_related_many_to_many/5,
   aggregate_related_many_to_many/7,
   find_related_many_first/4, find_related_many_max/5,
   find_related_many_range/6, aggregate_related_many/6,
   do_save/1, do_delete/1, field_names_for_query/0,
   field_names_for_query/1, field_to_iolist/1, set/3,
   db_pk_fields/0, get_pk_fk_fields/0, get_pk_fk_fields2/0,
   db_fields/0, db_field_names/0, db_field_names_str/0,
   db_field_names_bin/0, db_num_fields/0, id/1, id/2,
   name/1, name/2, birth_date/1, birth_date/2,
   instrument/1, instrument/2, bio/1, bio/2, new/4,
   driver/0, count/3, count/1, count/2, count_with/2,
   avg/3, avg/1, avg/2, avg_with/2, min/3, min/1, min/2,
   min_with/2, max/3, max/1, max/2, max_with/2, sum/3,
   sum/1, sum/2, sum_with/2, stddev/3, stddev/1, stddev/2,
   stddev_with/2, find/0, find/1, find_with/1,
   find_first/0, find_first/1, find_first_with/1,
   find_max/1, find_max/2, find_max_with/2, find_range/2,
   find_range/3, find_range_with/3]).

relations() -> erlydb_base:relations().

fields() -> erlydb_base:fields().

table() -> erlydb_base:table().

type_field() -> erlydb_base:type_field().

db_table() -> erlydb_base:db_table(musician).

db_field(FieldName) ->
    erlydb_base:db_field(musician, FieldName).

is_new(Rec) -> erlydb_base:is_new(Rec).

is_new(Rec, Val) -> erlydb_base:is_new(Rec, Val).

get_module(Rec) -> erlydb_base:get_module(Rec).

to_iolist(Recs) ->
    erlydb_base:to_iolist(musician, Recs).

to_iolist(Recs, ToIolistFun) ->
    erlydb_base:to_iolist(musician, Recs, ToIolistFun).

field_to_iolist(Val, _Field) ->
    erlydb_base:field_to_iolist(Val, _Field).

new() -> erlydb_base:new(musician).

new_with(Fields) ->
    erlydb_base:new_with(musician, Fields).

new_with(Fields, ToFieldFun) ->
    erlydb_base:new_with(musician, Fields, ToFieldFun).

new_from_strings(Fields) ->
    erlydb_base:new_from_strings(musician, Fields).

set_fields(Record, Fields) ->
    erlydb_base:set_fields(musician, Record, Fields).

set_fields(Record, Fields, ToFieldFun) ->
    erlydb_base:set_fields(musician, Record, Fields,
         ToFieldFun).

set_fields_from_strs(Record, Fields) ->
    erlydb_base:set_fields_from_strs(musician, Record,
             Fields).

field_from_string(ErlyDbField, undefined) ->
    erlydb_base:field_from_string(ErlyDbField, undefined).

save(Rec) -> erlydb_base:save(Rec).

insert(Recs) -> erlydb_base:insert(Recs).

update(Props) -> erlydb_base:update(musician, Props).

update(Props, Where) ->
    erlydb_base:update(musician, Props, Where).

delete(Rec) -> erlydb_base:delete(Rec).

delete_id(Id) -> erlydb_base:delete_id(musician, Id).

delete_where(Where) ->
    erlydb_base:delete_where(musician, Where).

delete_all() -> erlydb_base:delete_all(musician).

transaction(Fun) ->
    erlydb_base:transaction(musician, Fun).

before_save(Rec) -> erlydb_base:before_save(Rec).

after_save(Rec) -> erlydb_base:after_save(Rec).

before_delete(Rec) -> erlydb_base:before_delete(Rec).

after_delete({_Rec, Num}) ->
    erlydb_base:after_delete({_Rec, Num}).

after_fetch(Rec) -> erlydb_base:after_fetch(Rec).

find(Where, Extras) ->
    erlydb_base:find(musician, Where, Extras).

find_first(Where, Extras) ->
    erlydb_base:find_first(musician, Where, Extras).

find_max(Max, Where, Extras) ->
    erlydb_base:find_max(musician, Max, Where, Extras).

find_range(First, Max, Where, Extras) ->
    erlydb_base:find_range(musician, First, Max, Where,
         Extras).

find_id(Id) -> erlydb_base:find_id(musician, Id).

aggregate(AggFunc, Field, Where, Extras) ->
    erlydb_base:aggregate(musician, AggFunc, Field, Where,
        Extras).

count() -> erlydb_base:count(musician).

get(Idx, Rec) -> erlydb_base:get(Idx, Rec).

set_related_one_to_many(Rec, Other) ->
    erlydb_base:set_related_one_to_many(Rec, Other).

find_related_one_to_many(OtherModule, Rec) ->
    erlydb_base:find_related_one_to_many(OtherModule, Rec).

find_related_many_to_one(OtherModule, Rec, Where,
       Extras) ->
    erlydb_base:find_related_many_to_one(OtherModule, Rec,
           Where, Extras).

aggregate_related_many_to_one(OtherModule, AggFunc, Rec,
            Field, Where, Extras) ->
    erlydb_base:aggregate_related_many_to_one(OtherModule,
                AggFunc, Rec, Field, Where,
                Extras).

add_related_many_to_many(JoinTable, Rec, OtherRec) ->
    erlydb_base:add_related_many_to_many(JoinTable, Rec,
           OtherRec).

remove_related_many_to_many(JoinTable, Rec, OtherRec) ->
    erlydb_base:remove_related_many_to_many(JoinTable, Rec,
              OtherRec).

remove_related_many_to_many_all(JoinTable, OtherTable,
        Rec, Where, Extras) ->
    erlydb_base:remove_related_many_to_many_all(JoinTable,
            OtherTable, Rec, Where, Extras).

find_related_many_to_many(OtherModule, JoinTable, Rec,
        Where, Extras) ->
    erlydb_base:find_related_many_to_many(OtherModule,
            JoinTable, Rec, Where, Extras).

aggregate_related_many_to_many(OtherModule, JoinTable,
             AggFunc, Rec, Field, Where, Extras) ->
    erlydb_base:aggregate_related_many_to_many(OtherModule,
                 JoinTable, AggFunc, Rec, Field,
                 Where, Extras).

find_related_many_first(Func, Rec, Where, Extras) ->
    erlydb_base:find_related_many_first(Func, Rec, Where,
          Extras).

find_related_many_max(Func, Rec, Num, Where, Extras) ->
    erlydb_base:find_related_many_max(Func, Rec, Num, Where,
              Extras).

find_related_many_range(Func, Rec, First, Last, Where,
      Extras) ->
    erlydb_base:find_related_many_range(Func, Rec, First,
          Last, Where, Extras).

aggregate_related_many(Func, AggFunc, Rec, Field, Where,
           Extras) ->
    erlydb_base:aggregate_related_many(Func, AggFunc, Rec,
               Field, Where, Extras).

do_save(Rec) -> erlydb_base:do_save(Rec).

do_delete(Rec) -> erlydb_base:do_delete(Rec).

field_names_for_query() ->
    erlydb_base:field_names_for_query(musician).

field_names_for_query(UseStar) ->
    erlydb_base:field_names_for_query(musician, UseStar).

field_to_iolist(Val) ->
    erlydb_base:field_to_iolist(Val).

set(Idx, Rec, Val) -> setelement(Idx, Rec, Val).

db_pk_fields() ->
    erlydb_base:db_pk_fields([{erlydb_field, id, "id",
             <<105, 100>>, int, 11, integer, text_field,
             false, primary, undefined, identity}]).

get_pk_fk_fields() ->
    erlydb_base:get_pk_fk_fields([{id, musician_id}]).

get_pk_fk_fields2() ->
    erlydb_base:get_pk_fk_fields2([{id, musician_id1,
            musician_id2}]).

db_fields() ->
    erlydb_base:db_fields([{erlydb_field, id, "id",
          <<105, 100>>, int, 11, integer, text_field, false,
          primary, undefined, identity},
         {erlydb_field, name, "name", <<110, 97, 109, 101>>,
          varchar, 20, binary, text_field, true, undefined,
          undefined, undefined},
         {erlydb_field, birth_date, "birth_date",
          <<98, 105, 114, 116, 104, 95, 100, 97, 116, 101>>,
          date, undefined, date, text_field, true, undefined,
          undefined, undefined},
         {erlydb_field, instrument, "instrument",
          <<105, 110, 115, 116, 114, 117, 109, 101, 110,
            116>>,
          enum,
          [<<103, 117, 105, 116, 97, 114>>,
           <<112, 105, 97, 110, 111>>,
           <<100, 114, 117, 109, 115>>,
           <<118, 111, 99, 97, 108, 115>>],
          binary, select, true, undefined, undefined,
          undefined},
         {erlydb_field, bio, "bio", <<98, 105, 111>>, text,
          undefined, binary, text_area, true, undefined,
          undefined, undefined}]).

db_field_names() ->
    erlydb_base:db_field_names([id, name, birth_date,
        instrument, bio]).

db_field_names_str() ->
    erlydb_base:db_field_names_str(["id", "name",
            "birth_date", "instrument", "bio"]).

db_field_names_bin() ->
    erlydb_base:db_field_names_bin([<<105, 100>>,
            <<110, 97, 109, 101>>,
            <<98, 105, 114, 116, 104, 95, 100, 97, 116,
              101>>,
            <<105, 110, 115, 116, 114, 117, 109, 101,
              110, 116>>,
            <<98, 105, 111>>]).

db_num_fields() -> erlydb_base:db_num_fields(5).

id(Rec) -> erlydb_base:get(3, Rec).

id(Rec, Val) -> setelement(3, Rec, Val).

name(Rec) -> erlydb_base:get(4, Rec).

name(Rec, Val) -> setelement(4, Rec, Val).

birth_date(Rec) -> erlydb_base:get(5, Rec).

birth_date(Rec, Val) -> setelement(5, Rec, Val).

instrument(Rec) -> erlydb_base:get(6, Rec).

instrument(Rec, Val) -> setelement(6, Rec, Val).

bio(Rec) -> erlydb_base:get(7, Rec).

bio(Rec, Val) -> setelement(7, Rec, Val).

new(name, birth_date, instrument, bio) ->
    {musician, true, undefined, name, birth_date,
     instrument, bio}.

driver() ->
    erlydb_base:driver({erlydb_mysql,
      [{last_compile_time, {{1980, 1, 1}, {0, 0, 0}}},
       {outdir, "apps/music/ebin"}, debug_info, report_errors,
       report_warnings, {erlydb_driver, mysql}]}).

count(Field, Where, Extras) ->
    erlydb_base:aggregate(musician, count, Field, Where,
        Extras).

count(Field) ->
    erlydb_base:aggregate(musician, count, Field, undefined,
        undefined).

count(Field, Where) ->
    erlydb_base:aggregate(musician, count, Field, Where,
        undefined).

count_with(Field, Extras) ->
    erlydb_base:aggregate(musician, count, Field, undefined,
        Extras).

avg(Field, Where, Extras) ->
    erlydb_base:aggregate(musician, avg, Field, Where,
        Extras).

avg(Field) ->
    erlydb_base:aggregate(musician, avg, Field, undefined,
        undefined).

avg(Field, Where) ->
    erlydb_base:aggregate(musician, avg, Field, Where,
        undefined).

avg_with(Field, Extras) ->
    erlydb_base:aggregate(musician, avg, Field, undefined,
        Extras).

min(Field, Where, Extras) ->
    erlydb_base:aggregate(musician, min, Field, Where,
        Extras).

min(Field) ->
    erlydb_base:aggregate(musician, min, Field, undefined,
        undefined).

min(Field, Where) ->
    erlydb_base:aggregate(musician, min, Field, Where,
        undefined).

min_with(Field, Extras) ->
    erlydb_base:aggregate(musician, min, Field, undefined,
        Extras).

max(Field, Where, Extras) ->
    erlydb_base:aggregate(musician, max, Field, Where,
        Extras).

max(Field) ->
    erlydb_base:aggregate(musician, max, Field, undefined,
        undefined).

max(Field, Where) ->
    erlydb_base:aggregate(musician, max, Field, Where,
        undefined).

max_with(Field, Extras) ->
    erlydb_base:aggregate(musician, max, Field, undefined,
        Extras).

sum(Field, Where, Extras) ->
    erlydb_base:aggregate(musician, sum, Field, Where,
        Extras).

sum(Field) ->
    erlydb_base:aggregate(musician, sum, Field, undefined,
        undefined).

sum(Field, Where) ->
    erlydb_base:aggregate(musician, sum, Field, Where,
        undefined).

sum_with(Field, Extras) ->
    erlydb_base:aggregate(musician, sum, Field, undefined,
        Extras).

stddev(Field, Where, Extras) ->
    erlydb_base:aggregate(musician, stddev, Field, Where,
        Extras).

stddev(Field) ->
    erlydb_base:aggregate(musician, stddev, Field,
        undefined, undefined).

stddev(Field, Where) ->
    erlydb_base:aggregate(musician, stddev, Field, Where,
        undefined).

stddev_with(Field, Extras) ->
    erlydb_base:aggregate(musician, stddev, Field,
        undefined, Extras).

find() ->
    erlydb_base:find(musician, undefined, undefined).

find(Where) ->
    erlydb_base:find(musician, Where, undefined).

find_with(Extras) ->
    erlydb_base:find(musician, undefined, Extras).

find_first() ->
    erlydb_base:find_first(musician, undefined, undefined).

find_first(Where) ->
    erlydb_base:find_first(musician, Where, undefined).

find_first_with(Extras) ->
    erlydb_base:find_first(musician, undefined, Extras).

find_max(Max) ->
    erlydb_base:find_max(musician, Max, undefined,
       undefined).

find_max(Max, Where) ->
    erlydb_base:find_max(musician, Max, Where, undefined).

find_max_with(Max, Extras) ->
    erlydb_base:find_max(musician, Max, undefined, Extras).

find_range(First, Max) ->
    erlydb_base:find_range(musician, First, Max, undefined,
         undefined).

find_range(First, Max, Where) ->
    erlydb_base:find_range(musician, First, Max, Where,
         undefined).

find_range_with(First, Max, Extras) ->
    erlydb_base:find_range(musician, First, Max, undefined,
         Extras).

With this decompiled file, you get all things clearly behind the magic, such as, you have pair getter/setter functions for each field, for example:

Musician = musician:find({name, 'like' "Caoyuan Mus"}),
%% get the 'name' field of record Musician
Name = musician:name(Musician), 
%% set the 'name' field to "new name" and bind to a new record Musician1.
Musician1 = musician:name(Musician, "new name"),
%% Or,
Musician2 = musician:set_fields(Musician, {name, "new name"}, {birth_day, "1940/10/9"}),
%% then save one of them
musician:save(Musician2).

Finally, some notices for new comer to Erlang and Erlyweb:

  1. In Erlang, the Variable can only be bound(set value) once , so, only Musician1 and Musician2 have the "new name", Musician will keep the original value
  2. For efficiency reason, if the field is varchar/text type, the getter will return a binary rather than string, which can be printed on browser directly in Erlyweb, but, if you want to use it as a string, you can apply binary_to_list(Name) on it.
基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你类似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了三轴陀螺仪和三轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函数声明。它定义了类`MPU6050`,该类包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置和动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到类似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值