其实我们的 Mavericks 已经做得差不多了,但就是那个 View 总觉得还可以再更好,如果仔细看 Rails 的原始码,会发现有一个叫 ActionView 的部分,是用来处理有关 Template render,既然这样的话...,那最後两天就来做这个部分吧!
在开始之前,我们要先建立一些观念
经过将近一个月的实作,我们已经了解到 Rails 在实作 Controller 时会 new 一个 Controller 的物件出来,而每一个 Action 其实就是 Controller 的 mehtod,所以你会看到类似这样的程序码
# mavericks/lib/action_controller/metal.rb
module ActionController
class Metal
def process(action)
send action
end
# .
#.
# (略)
end
end
那 View 和 Controller 的关系又是什麽呢?我们都知道在 Ruby 里面,一个物件里面包含了 实体变数
,然後可以用物件的 实体方法
来操作 实体变数
,这样的观念相信大家都不陌生
就像下面的例子
class A
def initialize(name)
@name = name
end
def call
@name
end
end
puts A.new('apa').call
# apa
所以你可以这样想像
实体变数
时,我们可以将这个 实体变数
先取出来然後放到 View 的 实体变数
里面实体方法
实体方法
时,自然就会代入 实体变数
有点复杂?没关系,所以这里这里才要花上两天实作...XD,那我们先来搞清楚怎麽将 Controller 的 实体变数
取出来,放到 View 的 实体变数
里面,这里用的是 Ruby 的 instance_variables
,透过这个 method 我们可以将这个物件的 实体变数
给列出来
像是这样
# demo.rb
class Controller
def initialize(name)
@name = name
end
def index
@name
end
end
puts Controller.new('apa').instance_variables
# @name
取出来後要怎麽放到 View 的物件里面呢?我们可以先搭配 instance_variable_get
来取得值,并且转换成 Hash
像是这样
# demo.rb
class TasksController
def initialize(name)
@name = name
end
def index
@name
end
def render
assigns = {}
instance_variables.each do |name|
assigns[name[1..-1]] = instance_variable_get(name)
end
assigns
end
end
puts TasksController.new('apa').render
# {"name"=>"apa"}
再用另外一个很像的方法,叫 instance_variable_set
,来存放到 View 的物件里面,
# demo.rb
class ActionView
def initialize(assigns = {})
assigns.each_pair do |name, value|
instance_variable_set "@#{name}", value
end
end
end
class TasksController
def initialize(name)
@name = name
end
def index
@name
end
def render
assigns = {}
instance_variables.each do |name|
assigns[name[1..-1]] = instance_variable_get(name)
end
assigns
end
end
controller_instance_variable = TasksController.new('apa').render
action_view = ActionView.new(controller_instance_variable)
puts action_view.instance_variables
# @name
puts action_view.instance_variable_get(:@name)
# apa
这样我们就有步骤 1 的概念了
我们在步骤 2 有提到要将 .erb
档案里面的程序码,转成 View 的 实体方法
来执行,该怎麽做?我们可以用 include
搭配 module_eval
来实作到这点
一样直接看范例
# demo.rb
module CompiledTemplates
end
class Base
def compiled(method_name, code)
CompiledTemplates.module_eval <<-CODE
def #{method_name}
#{code}
end
CODE
end
end
class Template
include CompiledTemplates
end
Base.new.compiled('index_template', "'<h1>I am apa</h1>'")
puts Template.new.index_template
# <h1>I am apa</h1>
我们建立一个空的 Module,接着实作一个 Base
,里面有一个方法用来将 .erb
Template 里面的程序码转成另一个 Class 的 实体方法
,用法就是里面 module_eval
来「增加」CompiledTemplates 里面的 mehtod,接着 Template include 以後,就可以做到动态增加 instance method 的效果
有没有发现 module_eval
和前面所提到的 class_eval
和 instance_eval
很像?如果不清楚的话,建议透过多看几次官方的范例,来了解三者之间的差异
接着我们将前面两个步骤合在一起
# demo.rb
module CompiledTemplates
end
class Base def compiled(method_name, code)
CompiledTemplates.module_eval <<-CODE
def #{method_name}
#{code}
end
CODE
end
end
class ActionView
include CompiledTemplates
def initialize(assigns = {})
assigns.each_pair do |name, value|
instance_variable_set "@#{name}", value
end
end
end
class TasksController
def initialize(name)
@name = name
end
def index
@name
end
def render
assigns = {}
instance_variables.each do |name|
assigns[name[1..-1]] = instance_variable_get(name)
end
assigns
end
end
Base.new.compiled('index_template', "\"<h1>I am \#{@name}</h1>\"")
controller_instance_variable = TasksController.new('apa').render
action_view = ActionView.new(controller_instance_variable)
puts action_view.index_template
# <h1>I am apa</h1>
就完成 Rails 的 Template render 了!带着这个观念,明天就来实作在 Mavericks 上吧!
>>: 从零开始-30日练习开发iOS APP-铁人赛心得 Day-30
昨天说到了将资料订阅出来渲染在页面上的事,那麽就就来说说 RxJs 解订阅这件事吧。 这也是为了避免...
思路 用dps从start点走一遍,然後检查end点有没有finish。 程序码 class Sol...
接续昨天的题目 原本今天打算写完这题的,但一直卡在一个地方, 就先贴出我写到一半的成果吧 // ke...
databinding可用於将class的data与元件绑定,像是(findViewById、onC...
前言 GPU卡原来是针对游戏开发及显示加速的设计的,後来才扩散至挖矿、深度学习...等其他领域,而游...