浏览 6738 次
锁定老帖子 主题:为什么得不到变量?
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-04-19
class AdminController < ApplicationController @@b=455; def initialize @a=45; @@b=45666; end def list @t=food.find(...); end 我在list的view中使用 <%=@@b%>确得不到这个值,报错说: uninitialized class variable @@b in ActionView::Base::CompiledTemplates 在 view中可以使用 <%=@a%> 得到 @a的值。 可我在 initialize 方法中是初始化了的,到底应该怎样得到类变量? 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-04-20
谁知道?
|
|
返回顶楼 | |
发表时间:2007-04-22
分析了一下源码,分析结果比较长,为了让大家都看明白,先简要说下ERB吧,直接上代码:
require 'erb' a = 123 erb = ERB.new("it is <%=a%>") puts erb.result 输出 it is 123 require 'erb' a = 123 erb = ERB.new("it is <%=a%>") puts erb.src 输出的是"编译"后的ruby等效代码 _erbout = ''; _erbout.concat "it is "; _erbout.concat((a).to_s); _erbout require 'erb' flag = true erb = ERB.new(" <%if flag %> yes <% else %> no <% end %> ") puts erb.result 输出 yes ERB支持if,for等任意控制语句。 来个复杂点的: require 'erb' erb = ERB.new("<%if @flag %>yes<% else %>no<% end %>") class T attr_accessor :flag end method_name = "test" body = erb.src code = " def #{method_name} #{body} end " T.module_eval(code) t = T.new t.flag = true puts t.test t.flag = false puts t.test输出 yes no 这段代码动态向T类里添加test方法,方法体是一段ERB表达式的等效代码。 |
|
返回顶楼 | |
发表时间:2007-04-22
rails在执行action时是做了魔术的,将controller执行后的的成员变量全部复制到@template中,rhtml的render过程最终变成了@template._run_accounts_index_rhtml方法调用
整个过程滴水不漏(咋想起了狄仁杰?),让我们感觉全部执行过程都在controller上下文中一样,实际是在@template。 但它还是留下了马脚,没有复制类变量(@@xx),因而lyo的代码会出错。 下面是详细的分析 假设controller代码为: class AccountsController < ApplicationController def index @haha = 'just test' @accounts = Account.find(:all) end end accounts/index.rhtml内容: <%= @haha %> <% for account in @accounts %> ... <% end %> 每到达一个http请求,会new一个AccountsController的实例(controller), controller有一个ActionView::Base类型的成员变量(@template), @template又有一个Hash类型的成员变量(@assigns) 图示如下: controller(ActionController::Base) -> @template(ActionView::Base) -> @assigns(Hash) 具体过程是: 一、 new一个ActionController::Base实例controller,从而形成上面的实例关系图。 二、 调用controller的process,process又调用了perform_action。 三、 perform_action调用我们的action方法index,从而controller有了@haha和@accounts 四、 perform_action调用render_file方法。 五、 render_file调用add_instance_variables_to_assigns,将controller的成员以Hash的形式记录到@template的@assigns中,从而@assigns成了{'haha'=>'just test','accounts'=>xxx} 六、 render_file调用@template的render_file 七、 @template的render_file调用了compile_and_render_template 八、 compile_and_render_template调用compile_template 魔术开始了,首先说明下ActionView::Base定义中是include CompiledTemplates的: module CompiledTemplates end ActionView::Base include CompiledTemplates end compile_template代码的简化版本如下: method_name = 根据rhtml路径生成唯一的方法名,如`_run_accounts_index_rhtml' rhtml_content = 读取我们的rhtml文件内容,作为一个string body = ERB.new(rhtml_content).src code = "def #{method_name} #{body} end" CompiledTemplates.module_eval(code) 它会向CompiledTemplates中添加一个方法_run_accounts_index_rhtml,因为include的关系,它自然而然成了ActionView::Base(即@template)的方法。 九、 compile_and_render_template调用evaluate_assigns,从而使@template也有了@haha和@accounts,代码如下: @assigns.each { |key, value| instance_variable_set("@#{key}", value) } 十、 compile_and_render_template中调send(method_name),从而调用到上面生成的方法_run_accounts_index_rhtml,返回ERB执行结果。 十一、 controller的render_file调render_text内容输出给客户端 |
|
返回顶楼 | |
发表时间:2007-04-22
个人觉得真正需要用到@@xx的时候比较少,复制@@意义不大,如果有hook搞还行(不知道有没有?)
与lyo讨论后的结果是万不得已时改用$xx。 不管怎样,无论何时都运行在controller上下文的假象在这个问题中破灭了,算一个bug吧? 咋办泥? |
|
返回顶楼 | |
发表时间:2007-05-08
另外,$xx 就好使么?
做个实验: ruby 代码
每刷新一次页面,显示xx 的值自增了一。我用自带的webrick跑的。 不正常吗? 反问一句,正常吗? rails是每来一请求启一进程的,按理$xx也保持不了吧,咋还正常捏? 照理这些东东应该存在DB、memcached里吧。 另外,很多时候提到无状态,到底什么样是无状态? 可以解释成内存无状态,而把状态放到了DB、memcached中吗? |
|
返回顶楼 | |
发表时间:2007-05-09
$xx 应该是全局变量,是解释器启动时初始化好的,类似于jsp中的application,当然会每次加1了。
|
|
返回顶楼 | |
发表时间:2007-10-10
很经典的分析,忍不住赞一个
|
|
返回顶楼 | |
发表时间:2007-10-10
我的经验是,尽量少用全局变量。另外Rails生僻的用法也不要用,否则很容易碰到技术陷井,费时费力还不讨好。
|
|
返回顶楼 | |