下载地址

    SIZE: 14703381
    SHA1: b54f4633174dbc55db77d9fd6d0ef90cc35503af
    SHA256: 7aa247a19622a803bdd29fdb28108de9798abe841254fe8ea82c31d125c6ab26
    SHA512: 8b8dd0ceba65bdde53b7c59e6a84bc6bf634c676bfeb2ff0b3604c362c663b465397f31ff6c936441b3daabb78fb7a619be5569480c95f113dd0453488761ce7
    SIZE: 16799684
    SHA1: 6f4e99b5556010cb27e236873cb8c09eb8317cd5
    SHA256: 8c99aa93b5e2f1bc8437d1bbbefd27b13e7694025331f77245d0c068ef1f8cbe
    SHA512: 973fc29b7c19e96c5299817d00fbdd6176319468abfca61c12b5e177b0fb0d31174a5a5525985122a7a356091a709f41b332454094940362322d1f42b77c9927
    SIZE: 11990900
    SHA1: 943c767cec037529b8e2d3cc14fc880cad5bad8d
    SHA256: 27d350a52a02b53034ca0794efe518667d558f152656c2baaf08f3d0c8b02343
    SHA512: dd5690c631bf3a2b76cdc06902bcd76a89713a045e136debab9b8a81ff8c433bbb254aa09e4014ca1cf85a69ff4bcb13de11da5e40c224e7268be43ef2194af7
    SIZE: 20571744
    SHA1: fbebdd3a2a641f9a81f7d8db5abd926acea27e80
    SHA256: 8bf2050fa1fc76882f878fd526e4184dc54bd402e385efa80ef5fd3b810522e0
    SHA512: 5060f2dd3bfd271ef255b17589d6d014260d7ec2d97b48112b717ee01c62fe125c3fe04f813e02d607cea3f0a2a812b14eb3a28d06c2551354dfeff5f4c3dd6b

享受使用 Ruby 2.7 编制程序吧!

(文/开源中炎黄子孙民共和国卡塔尔    

Separation of positional and keyword arguments

器重字参数和任务参数的自发性转变,就要 Ruby 3 上校删除。

require "json"

json = <<END
{
  "name": "Alice",
  "age": 30,
  "children": [{ "name": "Bob", "age": 2 }]
}
END

case JSON.parse(json, symbolize_names: true)
in {name: "Alice", children: [{name: "Bob", age: age}]}
  p age #=> 2
end

复制代码 代码如下:

call the overridden method from the new

在 ruby 中重写两个艺术,新点子中还要调用未被重写前的旧艺术,有多少个手艺。

意气风发,世袭。必要改过每风姿浪漫处用到新章程的 reciever 。

class Foo
  def say
    'Hello'
  end
end

class Bar < Foo
  def say
    super + ' World!'
  end
end

Foo.new.say #=> 'Hello'
Bar.new.say #=> 'Hello World!'
# 把 reciever 从 Foo 改为 Bar

二,改进祖先链。那与持续雷同,但改正的矛头不肖似。

moudle Bar
  def say
    super + ' World!'
  end
end

class Foo
  prepend Bar
  def say
    'Hello'
  end
end

Foo.new.say #=> 'Hello World!'

# 使用了 prepend 把 Bar 放在 Foo 祖先链的下游,当寻找 say 方法时,首先找到 Bar 定义的 say 方法

三,使用
UnboundMethod
define_method

class Foo
  def say
    'Hello'
  end
end

# 在某处重新打开 Foo

class Foo
  old_say = instance_method(:say)
  define_method(:say) do
    old_say.bind(self)[] + ' World!'
    # 调用 instance_method 得到一个 UnboundMethod ,你需要在调用它之前 bind 一个 Foo 的实例
    # 前面说过调用 Proc#call 的三种方法,调用 Method#call 也是一样。这里采用了 [] ,你也可以用 .()
  end
end

四, alias 。正是 sinatra 接受的点子。

class Foo
  def say
    'Hello'
  end
end

# 在某处重新打开 Foo

class Foo
  alias :old_say :say
  def say
    old_say + ' World!'
  end
end

Foo.new.say #=> 'Hello World!'
Foo.new.old_say #=> 'Hello'
# 使用这种技巧,仍然可以访问旧的方法

越多的手艺,可参考这里。

继续看 Event#invoke 的达成,上面代码那行完成相配路线:

return unless pattern =~ request.path_info.squeeze('/')

String#squeeze
方法用单个字符替换连续现身的字符,用法很灵巧,参见文档。

sinatra 完成路线相称的参数相称的笔触是:

  • 将顾客优先定义的门径转变为正则表明式
  • 用那一个正则表明式去相称实际请求的路径
  • 若是相配成功,则把捕获的参数与概念的参数组成键值对保存起来

Event#initialize 完毕了门路转变正则表明式:

URI_CHAR = '[^/?:,&#\.]'.freeze unless defined?(URI_CHAR)
PARAM = /:(#{URI_CHAR}+)/.freeze unless defined?(PARAM)
SPLAT = /(.*?)/
attr_reader :pattern

def initialize(path, options = {}, &b)
  @path = URI.encode(path)
  @param_keys = []
  regex = @path.to_s.gsub(PARAM) do
    @param_keys << $1
    "(#{URI_CHAR}+)"
  end

  regex.gsub!('*', SPLAT.to_s)

  @pattern = /^#{regex}$/
end

第大器晚成把客户定义的路子编码成 UKugaI ,因为
rfc1738
文书档案规定在 ULANDL 中冒出的字符只可以是
字母和数字[0-9a-zA-Z]、一些特殊符号”$-_.+!*'(卡塔尔 以至部分封存字符:

only alphanumerics, the special characters “$-_.+!*'(),”, and
reserved characters sed for their reserved purposes may be used
unencoded within a URL.

假若在路径或询问参数中现身任何字符,比如普通话,需求先转义。

下一场把顾客在概念路线中的参数找寻来,替换为去掉冒号(:)后的正则表明式字符串。

PARAM 正则表达式———— /:([^/?:,&#\.]+)/————
匹配以冒号初始的,接下去的字符不是 / ? : , & # .
个中放肆一个字符的字符串。

$1 保存了近年二次正则表明式捕获的率先个门户差不离结果。

顾客仍为能够定义不具名参数: ‘*’
,这几个职能还不周详,现阶段只好作占位符用,没有办法获取捕获的参数。

接下去的职业就是把捕获的参数与概念的参数组成键值对保留在 params
中,此前的多种小说有说过。

保存好参数后,调用 Result.new(block, params, 200) 生成 Result ,它是
Struct 的实例。跟 OpenStruct 不同, Struct
只好读、写在早先化时设定的 key ,不能新增加 key :

Bar = Struct.new(a,b)
bar = Bar.new(1,2)
bar.a #=> 1
bar.c #=> undefined method `c' for #<struct Bar a=1, b=2>

sinatra 能科学响应 HEAD 央浼方法。依照 rfc
文档,
HEAD 方法跟 GET 方法唯大器晚成的区分便是,响应 HEAD 方法时,响应报文无法带有
body 。响应报文的头应该跟 GET 方法的同生机勃勃。 HEAD
方法主要用于注明财富的管事、可用性以至前段时间是还是不是修改过。

因此看来,即便是 HEAD 央浼, sinatra 会自动去找对应的 GET 方法回调:

(events[:get].eject(&[:invoke, request]) if method == :head)

在生成 HEAD 诉求的响合时,会安装 body 为空字符:

# line 839
body = '' if request.request_method.upcase == 'HEAD'

品质改良

  • JIT [试验性质]

    • 当优化纵然不成事时,JIT
      后的代码恐怕会被另行编写翻译到优化程度十分的低的代码。

    • 当方法(Method)被以为是纯函数(pure)时,会进展方式内联优化。这种优化措施仍然为实验性的,大多措施不被感到是纯函数。

    • --jit-min-calls 的暗中认可值从 5 调治到 10,000。

    • --jit-max-cache 的私下认可值从 1,000 调节到 100。

  • Symbol#to_s,Module#name,true.to_s,false.to_snil.to_s`
    今后一向重临一个结霜(frozen)字符串。重返的字符串始终和加以的指标相等。
    [实验性] [功能
    #16150]

  • CGI.escapeHTML 的品质被进步了。GH-2226

  • Compaction GC
  • Pattern Matching
  • REPL improvement
  • Separation of positional and keyword arguments

实际情况查看更新表明:

name_list = [“chareice”, “angel”]
name_list.map(&:upcase)
# => [“CHAREICE”, “ANGEL”]

重视提醒

早晚要先安装 1.8 版本的 ruby ,因为 1.9+ 的 ruby ,String
的实例是不响应 each 方法的,那会平素促成 rack 报错。可以运用
rvm 安装 1.8.7 版本的 ruby
,假设利用 rvm ,请先进级到最新版本,不然安装 1.8.7 的 ruby 时也会报错。

运用命令 git log -1 --format=%ai 0.2.0 ,查看 0.2.0 版本 sinatra
的“出厂日期”,获得 2008-04-11 16:29:36 -0700 ;而 1.8.7 版本的 ruby 是
二零零六 年 5 月发表的,两个宽容性应该相比较好。

列一下自己运转 sinatra 0.2.0 用到的 ruby 和重大 gem 的本子:

  • ruby-1.8.7-p374
  • rack 1.4.1
  • mongrel 1.1.5

Ruby 2.7.0
稳定版在圣诞节当天揭露了,此版本引进了无数新特征和性质改进,最值得注意的不外乎:

Ruby 2.7.0 preview 2 已经公布了,最后版本安顿在 三月宣布。该版本引进了某个新天性和属性改良,首假诺:

情势相配是函数式编制程序语言中朝齑暮盐选拔的特征,如果相称某叁个形式,它能够遍历给定的靶子并分配其值:

万一同时使用了&符号和扩散了block给一个函数,Ruby会报错。

lookup

接下去看 Application 的 call 方法。

首先由 lookup 方法落成基于诉求找到科学的路由。

def lookup(request)
  method = request.request_method.downcase.to_sym
  events[method].eject(&[:invoke, request]) ||
    (events[:get].eject(&[:invoke, request]) if method == :head) ||
    errors[NotFound].invoke(request)
end

sinatra 在 Enumerable 上扩展了 eject 方法,因为 Array 加载了
Enumberable 模块,所以 Array 实例能用 eject 方法。

def eject(&block)
  find { |e| result = block[e] and break result }
end

eject 方法内部,使用 find 方法找到第多个产生非 false 结果的 block
,并重回那一个结果。find 方法自然会回到第2个切合条件的要素,通过
break 能够订制本身的重临值。

这里 e 是 Event 的实例。 block 是由 Array 实例转变而来的 Proc 。

系列先是篇小说涉及过,
假诺跟在 & 后边对象的不是 Proc ,首先会调用那些目的的 to_proc
方法获得三个 Proc 实例,最终会调用那个 Proc 的 call 方法。

sinatra 扩展了 Array 的 to_proc 方法:

def to_proc
  Proc.new { |*args| args.shift.__send__(self[0], *(args + self[1..-1])) }
end

经过 to_proc 转换, Proc#call
把参数转变为叁个数组,把这么些数组第一个成分作为 receiver ,把调用
to_proc
方法的数组的第一个因素作为艺术,把四个数组余下的因素作为艺术的参数,拿前边的代码作例子:

# 在 lookup 方法里下面的这行代码

&[:invoke, request]

# 会得到这样一个 Proc

#=> Proc.new { |*args| args.shift.__send__(:invoke, *(args + [request])) }

# 在 eject 方法定义中

find { |e| result = block[e] and break result }

# block[e] 就是把 e 当作参数调用  Proc#call ,做的事情是: 以 `request` 作为参数,调用 `e` 的 `invoke` 方法。

block[e] 不可能写成 block(e) ,否则 ruby 会把 block 充任是 main
的三个措施来调用。有三种艺术能够调用
Proc#call

# 1
 a_proc.call()
# 2
 a_proc.()
# 3
 a_proc[]

紧凑 GC(Compaction GC)

紧密 GC 能够对碎片化的内部存款和储蓄器空间举办零散收拾。一些八线程 Ruby
程序或者会以致内部存款和储蓄器碎片,进而形成高内部存款和储蓄器使用率和进程下落。引进了
GC.compact
方法来降低堆,此函数压缩堆中的活动指标,以便能够选拔更加少的页,并且堆可能对
CoW 更友善。

case JSON.parse('{...}', symbolize_names: true)
in {name: "Alice", children: [{name: "Bob", age: age}]}
  p age
  ...
end
  • 职位参数和重要字参数的分别

幸存三个字符串列表,供给对此中的各种字符串推行转变大写操作,大家得以用多少个便捷写法来达成。

设置 body

application_test.rb 里有三个测量试验用举个例子下:

class TesterWithEach
  def each
    yield 'foo'
    yield 'bar'
    yield 'baz'
  end
end

specify "an objects result from each if it has it" do

  get '/' do
    TesterWithEach.new
  end

  get_it '/'
  should.be.ok
  body.should.equal 'foobarbaz'

end

设若未有在 get block 中安装 body 值, sinatra 就能用 block 的再次来到值作为
body ,要是这么些再次回到值不响应 each 方法, body
就能被设置为空字符。能够一成不改变这里的 TesterWithEach#each 完毕贰个轻巧的
each

class Foo
  attr_reader :bar

  def initialize(*bar)
    @bar = bar
  end

  def each
    return nil unless block_given?
    i = 0
    while i < bar.length
      yield bar[i]
      i += 1
    end
  end
end

# foo = Foo.new(1,2,3,4)
# foo.each { |i| p i  }

这几天结束, sinatra
的功底用都早已落到实处,剩下的扩大作用——如重定向、渲染xml/erb/sass/haml、传输文件等等——都以经过加载模块来实现。

其余值得关切的新特征

  • 方法援引运算符,.:,作为实验性意义步入了。功能
    #12125、功能
    #13581

  • 实验性地投入了把号码参数作为私下认可的块参数的特征。功能
    #4475

  • 无头范围实验性地参与了。它或者固然并未有最佳范围那么有用,但它对开拓DSL 是非常平价的。功能
    #14799

  ary[..3]  # identical to ary[0..3]
  rel.where(sales: ..100)
  • 新增了 Enumerable#tally,它会构思每种成分现身的次数。

  ["a", "b", "c", "b"].tally
  #=> {"a"=>1, "b"=>2, "c"=>1}
  • 允许在 self 上调用私有方法 [功能
    #11297] [功能
    #16123]

  def foo
  end
  private :foo
  self.foo
  • 新增 Enumerator::Lazy#eager。它会时有爆发三个非懒惰的迭代器。[功能
    #15901]

  a = %w(foo bar baz)
  e = a.lazy.map {|x| x.upcase }.map {|x| x + "!" }.eager
  p e.class               #=> Enumerator
  p e.map {|x| x + "?" }  #=> ["FOO!?", "BAR!?", "BAZ!?"]

REPL improvement 

irb 今后支撑多行编辑,它由 reline,readline 包容的纯 Ruby
完成驱动。它还提供 rdoc 集成。在 irb
中,能够体现给定类、模块或方法的援引。

(文/开源中国卡塔尔国    

那几个写法等同于

OpenStruct

值得细看的是在非开辟情状遭遇非常时的回调方法:

error do
  raise request.env['sinatra.error'] if Sinatra.options.raise_errors
  '<h1>Internal Server Error</h1>'
end

Sinatra.options 实际上是 OpenStruct 的实例。
OpenStruct
Hash 相近,但它经过元编程提供了过多便捷访谈、设置值的章程。
OpenStruct 用法比如:

# 1
person = OpenStruct.new
person.name    = "John Smith"
p person.name    #=> "John Smith"

# 2
person = OpenStruct.new(:name => "John Smith")
p person.name    #=> "John Smith"

七个简便版本的 OpenStruct 实现:

class OpenStruct
  attr_accessor :h
  def initialize(hash = {})
    @h = hash

    h.each do |key, value|
      self.class.send(:define_method, key) do
        h[key]
      end
      self.class.send(:define_method, "#{key}=") do |value|
        h[key] = value
      end
    end
  end

  def method_missing(m, *args)
    if args.size == 1
      # m is  :name=
      # change m to :name
      h[m.to_s.chop.to_sym] = args[0]
    elsif args.size == 0
      h[m]
    end
  end

  def respond_to?(m)
    h.respond_to?(m) || super
  end

end

require 'test/unit'

class TestOS < Test::Unit::TestCase
  def setup
    @person_1 = OpenStruct.new
    @person_2 = OpenStruct.new(:name => 'zhu')
  end

  def test_case_1
    assert_equal true, @person_1.respond_to?(:name)
    assert_equal nil, @person_1.name
    @person_1.name = 'zhu'
    assert_equal 'zhu', @person_1.name
  end

  def test_case_2
    assert_equal true, @person_2.respond_to?(:name)
    assert_equal 'zhu', @person_2.name
    @person_2.name = 'jude'
    assert_equal 'jude', @person_2.name
  end
end

如上只是自个儿胡思乱想写的, OpenStruct
的落到实处远远不是上面写的那么轻巧,有意思味能够看看源码。

Sinatra.options.raise_errors 的值只可以在代码里设置,当其值不为 nil 或
false
时,暗许在非开采景况下直接抛出十三分。要想在命令行运转时设置值,只要求在
load_options! 方法中加多风流倜傥行:

op.on('-r') { |env| default_options[:raise_errors] = true }

在订制开发条件下的特别和 404 页面时,使用到 %Q(...) 。 ruby
会特殊管理以百分号 ‘%’ 伊始的字符串,帮你节省不菲转义引号的难为:

The string expressions begin with % are the special form to avoid
putting too many backslashes into quoted strings.
出处

越来越多相同的用法见Ruby 里的 %Q, %q, %W, %w, %x, %r, %s,
%i。

在显示相当音信时,用 escap_html 来转义 &,<,>,/,',"
,把那一个 ascii 字符编码成实体编码,幸免 XSS 攻击,可是源码有注释说有 bug

On 1.8, there is a kcode = ‘u’ bug that allows for XSS otherwhise

源码中用正则表达式替换转义字符的实现值得参谋。

更加多关于 XSS
的文化,能够看看作者早先写的这篇。

其余自 2.6 版本的话明显的扭转

  • 有个别标准库已被更新
    • Bundler 2.1.0.pre.1
    • RubyGems 3.1.0.pre.1
    • CSV 3.1.2
      (NEWS)
    • Racc 1.4.15
    • REXML 3.2.3
      (NEWS)
    • RSS 0.2.8
      (NEWS)
    • StringScanner 1.0.3
    • 局地别的未有原本版本的库也被更新了。
  • 现行反革命开展块方法调用时,借使 Proc.new 和 proc 在未有 block
    会发生警报。

  • lambda 在措施调用时即使未有 block 会发生区块错误。

  • Unicode 和 Emoji 版本从 11.0.0 更新至 12.0.0。[功能
    #15321]

  • 改良 Unicode 至 12.1.0 版本,新扩充对于新岁号「令和」 U+32FF
    的支撑。[功能 #15195]

  • Date.jisx0301Date#jisx0301 和 Date.parse 呈现援救新的东瀛年号作为非正式的强盛成效,直到新的
    JIS X 0301 公布。[功能
    #15742]

  • 编写翻译器供给协助 C99 [杂项
    #15347] *至于方言使用的求实音讯请查阅:

见 NEWS 或 交给日志 以查看详细情况。

Pattern Matching(实验性)

减削 GC 能够对碎片化的内部存款和储蓄器空间进行零散收拾。一些十二线程 Ruby
程序或然会导致内存碎片,进而形成高内部存款和储蓄器使用率和速度回退。引进了
GC.compact
方法来压缩堆,此函数压缩堆中的活动目的,以便能够使用更加少的页,並且堆恐怕对
CoW 更友好。

capture_block(&p卡塔尔 { puts “传给一个block” }
#=>SyntaxError: (irb):30: both block arg and actual block given

从 at_exit 说起

还是从 at_exit 初步读代码。

$! 记录特别音讯,当调用 raise
的时候会安装那些变量,详见此处。

调用 load_options! 解释完运维参数后, sinatra 在装有条件设置碰到极度和
404 时的回调方法,在开采景况遭遇特别和 404
的回调方法比别的条件暴光更加的多的音信。

  • 格局相称(Pattern Matching)
  • REPL 改进
  • 紧凑 GC(Compaction GC)
  • 地方参数和要害字参数的分开

Compaction GC 

  • REPL 改进

复制代码 代码如下:

to_result

在获得响应的 body 时,无论是正常流程,依旧那么些流程,都调用了 to_result
方法。 sinatra 在超多类中都扩充了这一个实例方法。

正常流程的代码如下:

returned = run_safely do
  catch(:halt) do
    filters[:before].each { |f| context.instance_eval(&f) }
    [:complete, context.instance_eval(&result.block)]
  end
end
body = returned.to_result(context)
# 一切正常时, returned 是 [:complete, context.instance_eval(&result.block)]

与此相关的四个 to_result 方法是:

class Array
  def to_result(cx, *args)
    self.shift.to_result(cx, *self)
  end
end

class Symbol
  def to_result(cx, *args)
    cx.send(self, *args)
  end
end

returned.to_result(context) 最后是在 context 上调用 complete
方法,传入的参数是 context.instance_eval(&result.block) 的重临值。

极度流程,如在 before filters 中抛出 :halt ,在 README.doc
文书档案中详细表明了各种场地:

Set the body to the result of a helper method

throw :halt, :helper_method

Set the body to the result of a helper method after sending it
parameters from the local scope

throw :halt, [:helper_method, foo, bar]

Set the body to a simple string

throw :halt, ‘this will be the body’

Set status then the body

throw :halt, [401, ‘go away!’]

Set the status then call a helper method with params from local scope

throw :halt, [401, [:helper_method, foo, bar]]

Run a proc inside the Sinatra::EventContext instance and set the body
to the result

throw :halt, lambda { puts ‘In a proc!’; ‘I just wrote to $stdout!’ }

在好些个应对以上情状的 to_proc 中,值得大器晚成提的是以下那八个:

class String
  def to_result(cx, *args)
    args.shift.to_result(cx, *args)
    self
  end
end

class NilClass
  def to_result(cx, *args)
    ''
  end
end

throw :halt, 'this will be the body' 之后,最终会用到
String#to_result 方法,传入的参数唯有叁个 context ,因此 args
是个空数组, args.shift 得到 nil ,所以得扩张 NilClass#to_result
,但它什么也没做,径直重临空字符串。

相关文章