Ruby 从头开始写一个 Rails-like web 框架

suffering · 发布于 2014年9月12日 · 最后由 lzm420241 回复于 2016年4月22日 · 5343 次阅读
709
本帖已被设为精华帖!

最近阅读Rebuilding Rails时对自建ruby web framework有了兴趣, 就模仿rails的功能写了个.

目的不是再造个轮子, 我也没有这个能力, 主要是想在自己写框架的过程中理解rails的基本工作原理, 提升一下自己的水平. 这只是一个演示项目, 没有半点的实际使用价值, 唯一的作用就是便于阅读, 在阅读的过程中可以了解一些RAILS的基本原理.

项目地址: https://github.com/suffering/learnmvc

贴上部分README:

目标

这个项目的目的不是重写一个Ruby web framework, 而是在自己开发一个Rails-like framework时, 试着去理解一个ruby mvc style web framework的一些基本原理.

为什么需要

Rails的源码就是头恐怖的怪兽, 无数的童鞋倒前仆后继地打开源码, 而后倒下.

Rails看起来就像魔法, 运行rails s, 而后一切就开始运行了. 这些行为都是从哪里开始的? 客户端的请求到达后它如何接收?如何处理? 为什么在controller里可以调用 Model 里定义的模型? 为什么controller里的实例变量可以在view中运用? Rails是如何将erb文件渲染而后生成内容的? 它如何与layout协作并返回?如此种种.

对于新手来说, 这个就是魔法, 虽然很疑惑, 却是没有时间也没有能力去寻找这些答案. 同时, 新手也不需要去理解这些因为哪怕不理解, 只要懂得 rails 的规则, 把相应的东西写在相应的地方(MVC), rails 就会将它们组合在一起, 生成内容并打包发送.事实上, 很多人即使不了解这些, 只要对rails足够了解, 经验充足, 也可以写出很好的项目来.

但是这样是不够的, 只有更多地理解 rails 的内部原理, 才能更好地使用它, 写出灵活高效的代码.

已经实现的功能

  • 以rails MVC的方式组织代码, 以rails的规则命名controller, view, model等之后, 相互之间可以协作.
  • 自定义route, 使用match, resources, get, post, put, delete等方法自定义路由. 如 match '/products/:id' => 'products#show'.
  • 自定义database.yml, 使用activerecord作为ROM
  • 实现layout yield, content_for, yield, render, redirect_to等等
  • 实现静态文件serve.

注: 已实现的只是rails的九牛一毛. 展示的只是基本的调用链. 及MVC基本的结合方式. 许多功能诸如session, cache, secure, test, configable等都没有实现.

基本逻辑

  1. 一切从Rack开始. 几乎所有的ruby web framework都是rack app. Rack对象响应call方法, 返回三元素的array, 分别是status code, header, content body. 只要你的项目符合以上三个要求, 就是一个合法的rack app. 可以运行它, 在浏览器访问, 看到完整的响应内容. 所以, 主流程即是request与response的地程. 我们所做的事情就是在中间加入一些自己的东西.
  2. http request 到达 web server 后会即被rack封装, 而后你得到一个env对象. 它包含了客户的请求类型(get/post/put/delete/..), 请求的地址(env['PATH_INFO'], QUERY_STRING等等.
    1. 通过分析env, 我们知道客户的请求是指向哪个controller#action. 而后查看路由表我们的app能否响应此请求.
    2. 路由表在新建app对象时通过routes方法来定义, 具体的做法是接受一个block, block内调用match, get, post等方法时, 生成路由规则加入路由表. 路由表里包含路径字符串的匹配正则, controller, action, params等等.
    3. 参照上一条, 在路由规则中检查 env['PATH\_INFO'], 若匹配, 就知道了指向哪个controller的哪个action, 以及其params. 通过 ctrl_const = Object.const_get(params[:controller].capitalize)来得到相应的controller.
    4. 通过 ctrl_const.new(env).call(params[:action]) 可以调用到相应的方法.
    5. 到这一步, 已经初步描述了一个请求从客户端到服务器端并指向需要的controller#action的基本过程. 也即处理request的过程完成.
  3. Response的过程:
    1. 在ctrl_const.new上调用action后新的对象内就会拥有相应的实例变量. 这时通过Tiltgem, 按规则生成目标view的名字, 找到它, 而后render, render时将self作为scope传入. 至此view里可以调用action里所有的实例变量.Tilt.new(view).render self 得到了应该返回的html的内容.
    2. 上一步render得到的只是Controller#action对应的view, 需要将它交给layout处理.同样的使用Tilt, 将上一步得到的partial view 放到block中提交过去. 这样layout中的<%= yield %>关键字生效. 至此, 得到完整的html内容.
    3. Rack要求调用call后的返回的值是一个三值的array, 分别是[status code, head content, body].上一步得到的是html内容就是body部分.

如何使用和阅读本项目的源码

git clone https://github.com/suffering/learnmvc.git
cd learnmvc
#在编辑器中打开
cd srbmvc
bundle install
cd ../simpleapp
bundle install
rackup -p 3002
#通过rackup方式打开后, 代码在更改后不会自动重载, 可以考虑使用rerun
# gem install rerun
# rerun 'rackup -p 3002'
#以这种方式运行, 对任何文件的修改都会重载代码. 简单模拟rails的development mode.

Demo app运行后, 可以看到项目正常运行.

查看simpleapp的源码, 你会看到它的基本结构与rails app基本相同.

查阅srbmvc的源码, 按前文的基本逻辑栏来查看代码, 观看其调用链. 代码中有少量的注释,没有解释具体的细节, 只简单标注出此方法实现的目的与功能. 此部分代码的关键点在于从request开始后的调用链, route规则的指定与检查, controller#action的定位 以及render view 部分. 其他皆渣.

请将simpleappsrbmvc结合来阅读.

最好的学习方式是, 看完后, 自己写一个. 在写的过程中, 会发现很多以前注意不到的细节. 在实现rails的各种功能的过程中, 你必须对mvc的协作方式进行深入思考. 人在思考的过程中成长.

感谢@wikimo同学帮忙纠正错误~

共收到 22 条回复
3276
CoffeeDeveloper · #1 · 2014年9月12日

我来消灭0回复,谢谢分享_^

15
huobazi · #2 · 2014年9月13日

顺手把angular搞进去

709
suffering · #3 · 2014年9月13日

#2楼 @huobazi , 还没有支持API返回呢.

3673
limkurn · #4 · 2014年9月13日

40美刀~~~多厚?

709
suffering · #5 · 2014年9月13日

#4楼 @limkurn , 我只看了 Rebuilding Rails 的免费部分, 前几章而已. 穷啊, 40美刀买不起. 参考了这本书的初始架构, 其他的是我自己写的呢.

709
suffering · #6 · 2014年9月13日

此项目可部署: demo: http://learnmvc.zhuboliu.me/ 其中, products链接处不能访问, 是因为这里需要引用数据库.

3673
limkurn · #7 · 2014年9月13日

#6楼 @suffering 20 ~ 30 还能接受~~~

3672
simlegate · #8 · 2014年9月14日

至今Rack还未吃透~

9618
wosuopu · #9 · 2014年9月14日

$40,好贵的样子

12224
Guest · #10 · 2014年9月14日

👏

1107
jasl · #11 · 2014年9月15日

http://owningrails.com/ 也在讲同样的事情,不过是在线课程,讲师是Thin的作者 @xdite 有推荐 http://wp.xdite.net/?p=2407

709
suffering · #12 · 2014年9月15日

#11楼 @jasl 这个到更贵啊, $400+ 简直伤不起.

1107
jasl · #13 · 2014年9月15日

#12楼 @suffering 我感觉那课的精髓在于和作者的交流,单纯的实现框架(只实现核心DSL,不考虑性能)并不是很难

5210
badboy · #14 · 2014年9月16日

刚接触ruby的时候就被告知,核心是rack,一直在用,但是从不深入。。。

教我ruby的是一个叫罗昕的~~~哇嘎嘎

96
moon2l · #15 · 2014年9月17日 1 个赞

我找到个老版本的(2013.8.4),不过内容还很全(137页),大家可以下载看看,链接地址https://github.com/moon2l/Rebuilding-Rails

709
suffering · #16 · 2014年9月17日

#15楼 @moon2l ,3Q, 一直在找, 都没有找到. 谢谢啊谢谢 😄

1107
jasl · #17 · 2014年9月18日

#14楼 @badboy 那你还不过来参会 - -

96
wcp1231 · #18 · 2014年9月18日

#16楼 @suffering #15楼 @moon2l 我问作者要到一个优惠,然后4个人合买了。。。

9484
lonely21475 · #19 · 2014年9月23日

有时真的挺对Ruby底层代码感兴趣,但是却没有这个实力去掌控它

14154
hammer · #20 · 2014年10月13日
9484
lonely21475 · #21 · 2014年10月23日

#20楼 @hammer 谢谢您提供的资料!

96
lzm420241 · #22 · 2016年4月22日

#20楼 @hammer 这个链接讲ruby的原理,并非rails的,从LZ写的文章可以看书LZ更希望能理解rails的源码,不知道是不是我理解错了?我想更多的人是希望理解rails里面的机制把

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册