论坛首页 编程语言技术论坛

为什么得不到变量?

浏览 6755 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-04-19  
如果我的 controller的类这样写:
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 方法中是初始化了的,到底应该怎样得到类变量
   发表时间:2007-04-20  
谁知道?
0 请登录后投票
   发表时间: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表达式的等效代码。
0 请登录后投票
   发表时间: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内容输出给客户端
0 请登录后投票
   发表时间:2007-04-22  
个人觉得真正需要用到@@xx的时候比较少,复制@@意义不大,如果有hook搞还行(不知道有没有?)
与lyo讨论后的结果是万不得已时改用$xx。
不管怎样,无论何时都运行在controller上下文的假象在这个问题中破灭了,算一个bug吧?
咋办泥?
5 请登录后投票
   发表时间:2007-05-08  
另外,$xx 就好使么?
做个实验:
ruby 代码
  1. class TestController < ApplicationController
  2. def t
  3. $xx = 0 if not $xx
  4. render :text=>"xx is #{$xx}"
  5. $xx += 1
  6. end
  7. end

每刷新一次页面,显示xx 的值自增了一。我用自带的webrick跑的。
不正常吗?
反问一句,正常吗?
rails是每来一请求启一进程的,按理$xx也保持不了吧,咋还正常捏?
照理这些东东应该存在DB、memcached里吧。

另外,很多时候提到无状态,到底什么样是无状态?
可以解释成内存无状态,而把状态放到了DB、memcached中吗?
0 请登录后投票
   发表时间:2007-05-09  
$xx 应该是全局变量,是解释器启动时初始化好的,类似于jsp中的application,当然会每次加1了。
0 请登录后投票
   发表时间:2007-10-10  
很经典的分析,忍不住赞一个
0 请登录后投票
   发表时间:2007-10-10  
我的经验是,尽量少用全局变量。另外Rails生僻的用法也不要用,否则很容易碰到技术陷井,费时费力还不讨好。
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics