首页 分类 关于我
ruby
Nginx 配置示例 工程师的产品观 理理File/Dir/Pathname(一) TracePoint介绍 ruby中的return 如何动态改变某个class的祖先链 ObjectSpace介绍 Rails日志实现探索(3) Rails日志实现探索(2) Rails日志实现探索(1) Rails中的request rescue exception in ruby 设计模式之观察者模式 require 的故事 ruby中的编码 研究ruby的一些小技巧 Rails中间件 ruby对象的序列化 ActiveSupport宝藏之MessageVerifier 如何写rakefile Ruby on Rails 环境及准备 基于Rack的项目初始化
git
如何移除某次提交之前的版本历史 Git 不常用的好用的命令 Git高级技巧之忽略文件
database
Mysql数据库编码 从mongodb向mysql迁移数据
工具
搭建ipsec服务
linux
Linux 常用查看命令

如何动态改变某个class的祖先链

在rails中,一个Controller的祖先链中定义了数个名为process_action的方法。

分别位于如下module中:

  • ActionController::ParamsWrapper
  • ActionController::Instrumentation
  • AbstractController::Callbacks

在不同的模块中,方法process_action分别承载了一个功能,然后交处理通过super交给父类中的process_action继续处理。摘取其中一个方法的源码,如下:

# File actionpack/lib/action_controller/metal/params_wrapper.rb, line 232
def process_action(*args)
  if _wrapper_enabled?
    if request.parameters[_wrapper_key].present?
      wrapped_hash = _extract_parameters(request.parameters)
    else
      wrapped_hash = _wrap_parameters request.request_parameters
    end

    wrapped_keys = request.request_parameters.keys
    wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)

    # This will make the wrapped hash accessible from controller and view
    request.parameters.merge! wrapped_hash
    request.request_parameters.merge! wrapped_hash

    # This will display the wrapped hash in the log file
    request.filtered_parameters.merge! wrapped_filtered_hash
  end
  super
end

这是一个很有意思的利用ruby高级特性的设计。顺便提一下,我也是通过这个设计明白了,在ruby中为什么方法名相同,参数数目不相同并不被视作不同方法的一个好处了(否则这个黑魔法就不够好玩了)。

这个行为很像rack中中间件设计。我现在的需求是插入一个module到Controller的祖先链中,但我同样希望能够控制这个module的位置。

经过几天的思考,找到了解决办法,简单且巧妙。

ActionController::HttpAuthentication::Token::ControllerMethods.include Light::Instrumentation
ActionController::Base.include ActionController::HttpAuthentication::Token::ControllerMethods

即:首先在你想插入的位置所在的module中include你所自定义的module,然后在重新include一下所在位置的那个module。

下一个问题:Controller祖先链中的大部分module都extend了ActiveSupport::Concern,这个将当前模块的祖先链扁平化了?所以include的自定义模块并不能出现在祖先链中。如上例中,我好不容易才找了一个没有extend concern 模块的module。

© 2018 www.xinshengyin.com All rights reserved.

版权所有