目的:旨在了解元编程以及 Rails 的架构。
Rails 要求文件命名为下划线,类命名为驼峰,这样才会自动加载相应文件的原因:使用了 const_missing
和 const_get
方法。当调用了一个类名时,因为类名在ruby中是属于常量(大写),而又没有引进该文件的时候就会调用 const_missing
方法。
class Object
def self.const_missing(c)
return nil if @calling_const_missing
@calling_const_missing = true
require Rubyframework.to_underscore(c.to_s)
klass = Object.const_get(c)
@calling_const_missing = false
klass
end
end
此外,在通过路由地址获取controller和action的时候也使用到了const_get
方法。
def get_controller_and_action(env)
_, cont, action, after =
env['PATH_INFO'].split('/', 4)
cont = cont.capitalize
cont += 'Controller'
[Object.const_get(cont), action]
end
因为几乎所有的web框架都使用了rack作为App服务器,此书例子也不例外。所以需要定义一个Proc或者命名为call的方法。在输入地址后,rack就会调用call方法去处理逻辑。我们通过上面方法获取了controller和action后,怎么根据不同的controller和action去处理不同的逻辑呢?send
方法搞定!书中给出的例子如下:
klass, act = get_controller_and_action(env)
controller = klass.new(env)
text = controller.send(act)
ORM中在查询数据库时常常会使用 find_by_xxx()
的方法。在此书使用文件的json格式作为储存数据一节中,最后提到的练习题让你实现一个类似 ActiveRecord 的 find_all_by_xxx()
方法。我使用了 method_missing
并用正则匹配方法名,匹配了就遍历文件寻找。没有匹配就调用 super
方法使其抛异常。
def self.method_missing(method_sym, *arguments, &block)
if method_sym.to_s =~ /^find_all_by_(.*)/
array = []
FileModel.all.each do |hash|
if !hash[$1].nil? && hash[$1] == arguments[0]
array << hash
end
end
array
else
super
end
end
在渲染模版的时候,为了使在controller定义的实例变量能在模版里使用。我通过 instance_variables
和 instance_variable_get
方法获取所有实例变量并传到模版中。
def get_instance_variables
hash = {}
self.instance_variables.each do |attribute|
hash[attribute] = self.instance_variable_get(attribute)
end
hash
end
第七章练习题中,原来的hash是使用形式如hash['key']
来获取值的,但是Rails中是通过调用方法,即hash.key来获取值。此时可以使用 define_method
或者 method_missing
来实现。
# define_method 实现方式
@hash.each do |key, value|
define_method("#{key}") do
value
end
end
#method_missing 实现方式
def method_missing(name, *args, &block)
return @hash[name.to_s] if @hash[name.to_s]
super
end
最后一章适配路由中有个用到 instance_eval
方法的例子。我们知道这个方法会在一个对象的上下文执行一个块,把这个块称为
Context Probe。就像是一个深入到对象中的代码片段。
在测试到web应用中,就像使用rails到路由一样写了如下路由。
app = BestQuotes::Application.new
use Rack::ContentType
app.route do
match "", "quotes#index"
match "sub-app", proc { [200, {}, ["Hello, sub-app!"]] }
# default routes
match ":controller/:id/:action"
match ":controller/:id", default: { "action" => "show" }
match ":controller", default: { "action" => "index" }
end
而在routing.rb里,使用了 instance_eval
去调用match方法,此处就相当于 @route_obj.match("", "quotes#index")
,并依次调用
def route(&block)
@route_obj ||= RouteObject.new
@route_obj.instance_eval(&block)
end
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。