不管怎么样。今天介绍完 Ruby 的知识后,可能有一段时间不能继续介绍了。真的是...快腻烦了...
这周,发生了很多重大的事情,比如滴滴...一下子心情就不好了...
算了,我们还是继续了技术吧。
本章节我们将要介绍的是 Ruby 中的上下文绑定 ( Context Binding ) 。我们将会介绍以下几个知识点
Binding
类TOPLEVEL_BINDING
全局常量ERB
模板
Binding 类
Binding
类的实例一般用于封装接收器对象的 「 执行上下文 」 ( execution context )
执行上下文是指 「 给定代码段执行所需的整个环境 」。没有执行上下文的代码无法正确执行
执行上下文包括给定作用域范围内的变量、方法和 self
关键字
实际上,执行上下文比这还更复杂一点。无论如何,让我们保持简单,因为它不是本文的主题。
如果有空,我们将在额外的其它章节中详述这个关键概念
Binding
类不能公开实例化,因为它的 new
方法在 Binding 类初始化时是未定义状态。
因此,只能通过 Kernel#binding
方法在内部实例化此类
class Context def get_binding binding end end Binding.new # => NoMethodError: (undefined method `new' for Binding) Context.new.get_binding # => #<Binding:0x00007f7fda063130>
因为 Kernel#binding
方法是私有的,我们只能再定义一个方法 Context#get_binding
,并通过该方法从外部作用域访问 Context 类的内部 binding
方法
而我们的 Context 类的真正目的是: 封装执行上下文并能够从另一个执行上下文访问它
这段话可能有点让人难以理解,不管怎样,我们将在接下来的两节中详细介绍两个具体的例子。
TOPLEVEL_BINDING 全局常量
main
对象是最顶级范围。 Ruby 中的任何对象都至少需要在此作用域范围内被实例化
为了随时随地地访问 main
对象的上下文,Ruby 提供了一个名为 TOPLEVEL_BINDING
的全局常量
@a = 1 class Addition def add TOPLEVEL_BINDING.eval("@a += 1") end end Addition.new.add p TOPLEVEL_BINDING.receiver # => main p @a # => 2
上面这段代码中,Binding#receiver
方法返回 Kernel#binding
消息的接收者。为此,则保存了调用执行上下文 - 在我们的示例中,是 main
对象。
然后我们在 Addition
类的实例中使用 TOPLEVEL_BINDING
全局常量访问全局的 @a
变量
ERB 模板
我们通过你可能熟悉的 Ruby on Rails 中的 ERB
模板来分析 Binding 的用法
在 app/controllers/users_controller.rb
文件中
class UsersController < ApplicationController def show @user = "Mickael" end end
而在 app/views/users/show.html.erb
中
Hi <%= @user %>!
在上面这两个 Ruby on Rails 文件中,show.html.erb
模板可以访问 UsersController
类中的 @user
实例变量
这是如何做到的 ? 这背后的机制又是什么 ?
真相就是,ERB
类提供了一个 result
方法,该方法使用 Binding 类的实例作为参数
下面,让我们以更加手动的方式重新实现上一个示例,以使你了解背后发生的事情
class UsersController < ApplicationController def show @user = "Mickael" erb = ERB.new "Hi <%= @user %>!" puts erb.result(binding) # => Hi Mickael! end end
是不是很简单 ?
在这个示例中,ERB
实例可以通过 ERB#result
方法的参数传递的 Bind 类的实例访问UsersController
实例中定义的 @user
变量