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

Rails的怪异作法 -- 关于config

浏览 15044 次
该帖已经被评为精华帖
作者 正文
   发表时间:2006-11-21  

说ruby是怪异的语法有点不妥当,动态语言里面不乏这样的语法出现。但是看了一些源码后发现,使用ruby的用法真的各有不同,就像大家以前说的,ruby每个人写出来的可能都不同。

现来说Rails里面如何加载config的吧。

在java里面config绝对是一个resource文件,然后通过读取配置的工具加入进来,在分析处理。

在ROR里面完全不是这么回事。

1.首先大家要了解的是,在我们启动 ruby script/server 的时候,rails做了一系列的处理,最后他执行了environment.rb

ruby 代码
  1. Rails::Initializer.run do |config|   
  2.   # 这里能插入我们自己的配置。   
  3.   # config. 之类   
  4. end  

这里的config其实是Initializer内部的一个变量,掌控全局的配置信息,我们可以使用这个config来配置我们想要的。Rails::Initializer.run的源码是这样的,yield再一次显示了他的威力,让我们在配置文件中得以配置config。然后实例化了一个initializer 之后,把config作为参数传入了。

ruby 代码
  1. def self.run(command = :process, configuration = Configuration.new)   
  2.   yield configuration if block_given?   
  3.   initializer = new configuration   
  4.   initializer.send(command)   
  5.   initializer   
  6. end  

我们接着往下走,可以看到initializer 做了一系列的初始化工作,包括load_path的设定,路由的初始化,和activerecord的初始化。我们关心的还是配置如何起作用,那么来看看environments目录下面的配置文件是如何导入的吧。

ruby 代码
  1. def load_environment   
  2.   silence_warnings do  
  3.     config = configuration   
  4.     constants = self.class.constants   
  5.     eval(IO.read(configuration.environment_path), binding)   
  6.     (self.class.constants - constants).each do |const|   
  7.       Object.const_set(const, self.class.const_get(const))   
  8.     end  
  9.   end  
  10. end  

IO.read(configuration.environment_path) ,,这里就不使用什么回调不回调了,而是干脆IO拿出来eval一把,这里也是吃了一惊,这样也可以呀~~~~~~~然后,我们可以看看,他处理常量的方法,把自己配置文件中的常量全部放入Object里面,起到全局常量的目的。

最绝的还是initialize_framework_settings,使用了有一个ruby的技巧。

ruby 代码
  1. def initialize_framework_settings   
  2.   configuration.frameworks.each do |framework|   
  3.     base_class = framework.to_s.camelize.constantize.const_get("Base")   
  4.   
  5.     configuration.send(framework).each do |setting, value|   
  6.       base_class.send("#{setting}=", value)   
  7.     end  
  8.   end  
  9. end  

configuration.frameworks里面存放的是rails个个组件的名字,比方说active_record之类。然后把这个名字大写转换,然后用constantize取得ActiveRecord这个Module(注意,这些东西都在activesupport里面呢,activesupport/lib/active_support/core_ext/string/inflections.rb )。然后用const_get取得这个模块的Base类,也就是ActiveRecord::Base这个类了(下面都叫做Base类),所有的Rails的组件都是这个命名规则改天我们自己想要做一个Rails的组件加进来,也可以这样(但是要稍微修改一个源码)。

然后,我们吧config里面的内容给Base类。configuration.send(framework)是调用一个组件名称的方法,比方说active_record,就是去的config里面的active_record属性(这是最基本的),通过后面的do我们可以看到config返回的是一个hash,然后把hash中每一个key作为变量,value为传入值,传入Base类。。。这里大家应该没什么问题了,看看我们的config文件是怎么写的吧。

ruby 代码
  1. # Settings specified here will take precedence over those in config/environment.rb   
  2.   
  3. # In the development environment your application's code is reloaded on   
  4. # every request.  This slows down response time but is perfect for development   
  5. # since you don't have to restart the webserver when you make code changes.   
  6. config.cache_classes = false  
  7.   
  8. # Log error messages when you accidentally call methods on nil.   
  9. config.whiny_nils = true  
  10.   
  11. # Enable the breakpoint server that script/breakpointer connects to   
  12. config.breakpoint_server = true  
  13.   
  14. # Show full error reports and disable caching   
  15. config.action_controller.consider_all_requests_local = true  
  16. config.action_controller.perform_caching             = false  
  17. config.action_view.cache_template_extensions         = false  
  18. config.action_view.debug_rjs                         = true  
  19.   
  20. # Don't care if the mailer can't send   
  21. config.action_mailer.raise_delivery_errors = false  

哦,看着很晕吧,config就是我们的配置对象,按照我们上面的说法,config.action_view之类framework的变量应该是一个hash才对呀,如果是hash的话,不应该用这样的方式传入,可能会用 config.action_view = {:debug_rjs => true}来传入。

OK.我们来看这个变量到底是什么样的hash。

ruby 代码
  1. def initialize   
  2.   ....   
  3.   ....   
  4.   for framework in default_frameworks   
  5.     self.send("#{framework}=", OrderedOptions.new)   
  6.   end  
  7. end  

在初始化这些变量的时候,Rails给他赋值为OrderedOptions.new。这个特殊的类型可能就是关键。

ruby 代码
  1. class OrderedOptions < OrderedHash #:nodoc:   
  2.   def []=(key, value)   
  3.     super(key.to_sym, value)   
  4.   end  
  5.      
  6.   def [](key)   
  7.     super(key.to_sym)   
  8.   end  
  9.   
  10.   def method_missing(name, *args)   
  11.     if name.to_s =~ /(.*)=$/   
  12.       self[$1.to_sym] = args.first   
  13.     else  
  14.       self[name]   
  15.     end  
  16.   end  
  17. end  

看到其中的玄妙了么,method_missing~~~!! 如果调用一个**=的方法 ,就像当用传入一个HASH的值,key就是方法的名字。

也就是:config.action_view.debug_rjs  = true 相当于config.action_view[:debug_rjs] = true

OK ,大体上描述了一下,可以看到简单的一个Rails初始化已经给我们展示了几乎全部ruby的靓丽之处,这能说明,这个亮点肯定是贯穿rails的基本,在以后的深入研究中我们就能看到了。

   发表时间:2006-11-21  
method_missing是个亮点。eval也很灵活。用编程的方式进行配置挺好。差沙好文呀。
0 请登录后投票
   发表时间:2006-11-21  
发挥到极致了。
0 请登录后投票
   发表时间:2006-11-22  
写得很好阿,学习
0 请登录后投票
   发表时间:2006-11-22  
Give me a reason why this is superior than java properties file? Is it pragmatic? Is it necessary?
0 请登录后投票
   发表时间:2006-11-22  
I never said this is superior than java properties file.
I only want to show the Rails Way.
0 请登录后投票
   发表时间:2006-11-22  
差沙 写道
I never said this is superior than java properties file.
I only want to show the Rails Way.

Sorry, I am not saying you are not pragmatic. I mean is there anyone can give me a good reason this is way can bring me more value than java way?
0 请登录后投票
   发表时间:2006-11-22  
哇塞,
都开始飙英文,
好样的,学习下,
继续
0 请登录后投票
   发表时间:2006-11-22  
没办法,本来我也想用英语回复的,但是我的系统上没有“英语输入法”,呵呵~!
0 请登录后投票
   发表时间:2006-11-22  
mafa 写道
没办法,本来我也想用英语回复的,但是我的系统上没有“英语输入法”,呵呵~!
俺不是想飙英文,上班用客户机器,没有中文输入法。
0 请登录后投票
论坛首页 编程语言技术版

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