大家好:
R队上一篇中讲到了如何通过分析请求来定位到我们要修改的模块
但是目前我们只是定位到了代码的位置,还没有具体分析方法的调用关系,今天我们要进阶一点,通过一个提交工单时缓存目标版本的功能为例,弄清楚一个功能的实现具体调用了哪些方法,在弄清楚Redmine的方法调用之后,我们自然就知道应该把我们的二次开发代码放在哪个位置了。
一. 原理分析
回顾上一篇的内容,我们已经定位到的 IssuesController的 new方法,代码如下:
def new respond_to do |format| format.html { render :action => 'new', :layout => !request.xhr? } format.js end end
可以看到,在new方法里没有调用任何逻辑,但是常识告诉我们,要new一个Issue,没有任何业务逻辑是不可能的,肯定在什么地方隐含了一些我们目前还没发现的逻辑。
答案就是,在 IssuesController文件的开头,有这么一段代码:
before_filter :authorize, :except => [:index, :new, :create, :get_case_params, :console_log] before_filter :find_optional_project, :only => [:index, :new, :create] before_filter :build_new_issue_from_params, :only => [:new, :create]
从原理上说,这里是通过类宏为Controller注册了一系列钩子方法,感兴趣的同学可以阅读《ruby元编程》来了解详细知识。不过目前我们只需要知道,这样做的效果就是:Controller在调用new方法之前,先调用了 before_filter 注册的方法。
注意before_filter的语法,before_filter这个名字表示这些方法是在调用 new之前被执行的。 第一个参数:authorize是要调用的方法的名字,第二参数有两种:
before_filter :authorize, :except => [:index, :new, :create, :get_case_params, :console_log]
except表示除了 index、new等等这些action以外,其他的任意action执行前都需要先执行authorize
before_filter :find_optional_project, :only => [:index, :new, :create]
only则正好相反,表示只有index、new、create在调用前需要先执行find_optional_project
并且,如果有多条语句为一个action定义了before_filter,这些被注册的方法会按照先后顺序依次执行,全部完成后才会执行new。
二. 实现方法
现在我们知道了,原来在new一个Issue之前,需要先执行这些额外的方法,这些方法的定义,要么来自于Helper,要么继承自所有Controller的父类ApplicationController,或者直接定义在controller自身之中。
现在我们用一个例子来实践一下:提bug时根据缓存值,自动选择目标版本。要实现这个需求,其实只需要在controller中new一个Issue之前,先给目标版本这个字段赋值即可。那么今天所学就派上用场啦。
实现此功能的最佳方式,当然是我们自己定义一个before_filter了:
before_filter :issue_attrs_filter, :only => [:new,:edit]
在Issue new之前,先调用我们的issue_attrs_filter方法。
接下来我们要自己定义一下issue_attrs_filter这个方法,直接写在issuesController中即可:
def issue_attrs_filter ...#逻辑处理 end
这个功能就实现了,就这么简单,对其他功能的代码没有任何影响,并且如果哪天我们不需要这个功能了,只需要注释一行代码即可:
# before_filter :issue_attrs_filter, :only => [:new,:edit]