rails sinatra 等一些ruby的web框架,都是属于rack app, 建立在rack基础上的。
rack是什么?官网上的解释是Rack provides a minimal interface between webservers supporting Ruby and Ruby frameworks.我的理解就是链接服务器和ruby应用框架的,它解析http,rack里有两个比较重要的class, Rack::Request, Rack::Response,Rack::Request 负责解析request, Rack::Response负责http的response,想了解具体rack的应用查看http://www.rubydoc.info/github/rack/rack/Rack/Mime。
rack可以use多个middleware和run一个app, middleware一般如下格式:

class MyMiddlewaredef initialize(app)@app = appenddef call(env)puts 'mymiddleware'puts env.inspectif env['PATH_INFO'] == '/'@app.call(env)else[404, {'Content-Type' => 'text/plain'}, ['not ok']]endendend

run的那个app要响应call,可以如下:

my_app = proc do |env|[200, {'Content-Type' => 'text/plain'}, ["ok"]]end

那么就可以用rack在config.ru中这样写服务,如下:

use MyOtherrun my_app

以上只是展示了rack的感念,以及rack写服务的基础。因为sinatra是建立在rack基础之上的,所有这些应该算是读sinatra源码的前提准备。
1.sinatra的整体结构
那么,我们来看一下,sinatra是怎样来应对rack的要求的,sinatra大体结构是这样的,代码如下:

module Sinatraclass Base# 用于作为middleware用def initialize(app=nil)super@app = appyield self if block_given?enddef call(env)dup.call!(env)enddef call!(env)endclass << self# 用于作为app用def call(env)prototype.call(env)enddef prototype@prototype ||= newendendendend

以上的代码结构就是sinatra应对rack的要求而组织的,使得sinatra既可以作为middleware又可以作为app,当然,sinatra要多一些magic,后面再说,如果只是这样的结构,sinatra程序就只能写成modular-style structure,不能写成classic style 的structure,对于modular-style structure 我写了一seed https://github.com/wpzero/sinatra_seed.gitclone下来看一下。,可以
下面我们来看,sinatra路径magic get/post/put/delete
2.sinatra 路径 class macro
sinatra 应用的代码如下:

require "sinatra/base"class UserController < Sinatra::Baseget '/hello' do'hello'endend

这个get方法就是sinatra的一些magic,用class macro实现的。
class macro, 涉及到一些meta programming的概念,我这里稍微介绍一下
ruby 为什么有singleton method?
例如:

class C < Dendc1 = C.newc2 = C.newdef c1.helloputs 'hello'endc1.helloc2.hello

#=> 'hello'
#=> undefined method `hello' for #<C:0x007fe5442665c0>
这是为什么?c1和c2不是属于一同一个class吗?c1 look up method一定也是向右一步找到它的class(C),然后一路向上找class C的ancestors, 找是否有hello这个instance_method没,有就调用,可是c2为什么不能找到hello这个method,原因是ruby,在instance和它的class之间还有一层class(也就是大家所说的eigenclass),每一个 instance和它的eigenclass是唯一对应的。这就是hello这个方法的悉身之所,而instance c2的eigenclass与c1的eigenclass不同,所以c2不可找到hello。
有了eigenclass这个概念,我们就可以利用往eigenclass中加入 instance method,那么相应的instance就可以调用了。而class也是一个instance 只不过它的class是Class而已。
具体关系如图:

那么我们就来看一个最简单的class macro。

class Cclass << selfdef macroputs 'macro'endendmacroend

#=> macro

在class C 的eigenclass中定义一个方法,那么class C就可以调用。
那么sinatra是怎么实现get/put这些routes 的方法的?
大体结构如下:

class Baseclass << selfdef get#具体内容enddef post#具体内容endendend

这样我们就可以调用get等方法在Base的subclass中。
下面我们具体分析它的实现的代码。
3.sinatra get 的具体实现(unboundmethod)
先介绍一下unboudmethod。
一般instance method都是属于某个class,而调用它的是该class的instance,那么该在instance method中就可以access该instance的instance variable。那么我们可不可以把一个instance method 指定给一个instance?ruby是支持这个功能的,那就是bind。
先看一个例子:

class Squaredef area@side * @sideenddef initialize(side)@side = sideendendarea_un = Square.instance_method(:area)s = Square.new(12)area = area_un.bind(s)area.call   #=> 144

instance_method Returns an UnboundMethod representing the given instance method in mod.
而这有什么用呢?我们就可以把一些method存起来,在某一时刻bind给一个instance来执行。
联想一下sinatra的magic get方法,其实,它就是实现了在url 的path_info符合某个路径时,执行传给get的block,而且这个block是bind到一个Application instance上的(也就是前边的举的例子的UserController的instance)。
那么来看一下sinatra的源码,如下:

class Baseclass << selfdef get(path, opts={}, &block)route('GET', path, opts, &block)enddef route(verb, path, options={}, &block)signature = compile!(verb, path, block, options)(@routes[verb] ||= []) << signaturesignatureenddef compile!(verb, path, block, options = {})method_name             = "#{verb} #{path}"unbound_method          = generate_method(method_name, &block)# string to regular expression, and get the route keyspattern, keys           = compile pathconditions, @conditions = @conditions, [][ pattern, keys, conditions, block.arity != 0 ?proc { |a,p| unbound_method.bind(a).call(*p) } :proc { |a,p| unbound_method.bind(a).call } ]end# path to regular expression and keysdef compile(path)keys = []if path.respond_to? :to_strpattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c) }pattern.gsub!(/((:\w+)|\*)/) do |match|if match == "*"keys << 'splat'"(.*?)"elsekeys << $2[1..-1]"([^/?#]+)"endend[/^#{pattern}$/, keys]elsif path.respond_to?(:keys) && path.respond_to?(:match)[path, path.keys]elsif path.respond_to?(:names) && path.respond_to?(:match)[path, path.names]elsif path.respond_to? :match[path, keys]elseraise TypeError, pathendend# 产生Base 的unboundmethoddef generate_method(method_name, &block)# the current class is Base, self is Basedefine_method(method_name, &block)method = instance_method method_nameremove_method method_namemethodendendend

这里generate_method用于把一个block变为Base的unboundmethod用的就是define_method加上instance_method。

转载于:https://my.oschina.net/wpzero/blog/365489

sinatra源码解读相关推荐

  1. Bert系列(二)——源码解读之模型主体

    本篇文章主要是解读模型主体代码modeling.py.在阅读这篇文章之前希望读者们对bert的相关理论有一定的了解,尤其是transformer的结构原理,网上的资料很多,本文内容对原理部分就不做过多 ...

  2. Bert系列(三)——源码解读之Pre-train

    https://www.jianshu.com/p/22e462f01d8c pre-train是迁移学习的基础,虽然Google已经发布了各种预训练好的模型,而且因为资源消耗巨大,自己再预训练也不现 ...

  3. linux下free源码,linux命令free源码解读:Procps free.c

    linux命令free源码解读 linux命令free源码解读:Procps free.c 作者:isayme 发布时间:September 26, 2011 分类:Linux 我们讨论的是linux ...

  4. nodeJS之eventproxy源码解读

    1.源码缩影 !(function (name, definition) { var hasDefine = typeof define === 'function', //检查上下文环境是否为AMD ...

  5. PyTorch 源码解读之即时编译篇

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 作者丨OpenMMLab 来源丨https://zhuanlan.zhihu.com/ ...

  6. Alamofire源码解读系列(九)之响应封装(Response)

    本篇主要带来Alamofire中Response的解读 前言 在每篇文章的前言部分,我都会把我认为的本篇最重要的内容提前讲一下.我更想同大家分享这些顶级框架在设计和编码层次究竟有哪些过人的地方?当然, ...

  7. Feflow 源码解读

    Feflow 源码解读 Feflow(Front-end flow)是腾讯IVWEB团队的前端工程化解决方案,致力于改善多类型项目的开发流程中的规范和非业务相关的问题,可以让开发者将绝大部分精力集中在 ...

  8. spring-session源码解读 sesion

    2019独角兽企业重金招聘Python工程师标准>>> spring-session源码解读 sesion 博客分类: java spring 摘要: session通用策略 Ses ...

  9. 前端日报-20160527-underscore 源码解读

    underscore 源码解读 API文档浏览器 JavaScript 中加号操作符细节 抛弃 jQuery,拥抱原生 JS 从 0 开始学习 GitHub 系列之「加入 GitHub」 js实现克隆 ...

最新文章

  1. 7个小众却很有意思的工具推荐,每一个都是大宝藏!
  2. maven 笔记,概念
  3. openwrt信号弱掉线_斐讯 FIR151M 频繁掉线(OpenWRT解决方案)
  4. 面试之JSP九大内置对象和JSP四大作用域
  5. ArchLinux On Win10
  6. 伪装nginx版本防止***web服务器
  7. my资源列表一 (csdn 99% 0分资源下载)
  8. 键盘哪个键是锁定计算机,键盘锁是哪个键 锁键盘的是哪个键 - 云骑士一键重装系统...
  9. 蓝桥杯算法竞赛系列第五章——拔高篇之深度优先搜索(DFS)
  10. IText_根据模板导出PDF(文字、表格、图片)
  11. JAVA的学习心路历程之JDK基础入门(上)
  12. C#毕业设计——基于C#+asp.net+sqlserver的WEB招投标系统设计与实现(毕业论文+程序源码)——招投标系统
  13. 整数、长整型、浮点型、字符串
  14. JavaScript面试问题:事件委托和this
  15. 6个座位办公室最佳位置_6个座位办公室最佳位置,要注意咯!
  16. 网络通信协议基础(ISIS)——架构
  17. NB-IoT和LoRa在电力无线专网中的应用
  18. springmvc 下载文件ie8可用
  19. select() 函数
  20. 100以内不能被3整除的数

热门文章

  1. 在飞书上定制 BOT 服务,Chatopera 机器人让办公更智能 | Chatopera
  2. 梦龙LinkWorks协同办公平台 几处突破点
  3. el-table-column设置表格内居中
  4. 行业要闻丨巨额研发投入助力量子技术发展
  5. ​windows系统——更改系统开机音效
  6. 自动化测试识别验证码方法
  7. 音质好的骨传导蓝牙耳机有哪些,十大公认音质好的骨传导耳机
  8. 京东量化教你如何用简单的策略回测盈利
  9. 矩形选框工具和矩形工具的区别
  10. 【CCNA考试不过能重考吗?】