useful methods in rails rails3 half

1)ActiveSupport::OrderedOptions.new
其实就是一个hash,但是使用更加OO的方式

h = ActiveSupport::OrderedOptions.new
#赋值
h.boy = "john"

#输出
h.boy = "john"

2)extract_options!
是最最常见的方法,作用在于从参数组中抽出hash,参数组哈希分离

def options(*args)
  args.extract_options!
end

options(1, 2)           # => {}
options(1, 2, :a => :b)  # => {:a=>:b}

3)ActiveSupport::Concern
这个是非常有用的模块,特别是写插件的时候。在rails3之前,我们给一个类扩展实例方法(instance_methods)和单例方法(class_methods)的方法如下:

module M
def self.included(base)
  base.extend, ClassMethods
  base.send(:include, InstanceMethods)
  scope :disabled, where(:disabled => true)
end

module ClassMethods
def aa
  p "我是class_methods"
end
...
end

module InstanceMethods
def bb
  p "我是instance_methods"
end
...
end
end

# 使用
class Foo
  include M
end

# 测试
foo = Foo.new
foo.aa #=>NoMethodsFind
Foo.aa #=> "我是class_methods"
foo.bb #=>"我是instance_methods"

在rails3 中如下写:

require 'active_support/concern'
module M
extend ActiveSupport::Concern
included do
scope :disabled, where(:disabled => true)
end

module ClassMethods
...
end

module InstanceMethods
...
end
end
# 剩下的类似

使用ActiveSupport::Concern还有一个最大的好处是它可以处理include的依赖关系

require 'active_support/concern'
module Foo
extend ActiveSupport::Concern
included do
class_eval do
def self.method_injected_by_foo
...
end
end
end
end

module Bar
extend ActiveSupport::Concern
include Foo

included do
self.method_injected_by_foo
end
end

class Host
include Bar # 这边只要include Bar就可以了,而不需要include Foo模块
end

# 更详细的看代码 concern

4)delegate
源码
代理用在active_record上特别有效,提供了极大的方便。

class Foo < ActiveRecord::Base
  def hello
    "Hello"
  end

  def world
    "World"
  end
end

class Brr < ActiveRecord::Base
belongs_to :Foo
# 使用delegate 将方法a代理给类B
# 至少代理一个方法,to选项必填
delegate :hello, :to => :foo
end

# 测试
Brr.new.hello # => "Hello"
Brr.new.world # => NoMethodError

还可以代理实例变量,类变量,常量,还可以带allow_nil和prefix选项,具体看api文档了。

5)alias_method_chain和alias_attribute
源码
alias_method_chain算是rails中一个比较重要的方法。具体看这篇文章alias_method_chain_rails3
alias_attribute 给属性定义别名的,api中的例子:

class Content < ActiveRecord::Base
# has a title attribute
end

class Email < Content
alias_attribute :subject, :title
end

e = Email.find(1)
e.title    # => "Superstars"
e.subject  # => "Superstars"
e.subject? # => true
e.subject = "Megastars"
e.title    # => "Megastars"

说两点,第一,alias_attribute接受的参数,第一个是new_name,第二个是old_name;
第二,new_name和old_name捆绑了,一个改变会带动另一个改变,另外alias_attribute会生成四个以new_name为前缀的方法,如下:

Email.instance_methods(false)
# => ["_changed?", "_change", "_will_change!", "_was"]
e.subject_changed? #=> true
e.subject_change #=> ["", "Megastars"]

========待续===============

rails alias_method_chain rails3

ruby中的alias方法

class Foo
  def hello
    "Hello"
   end

  def world
    "World"
  end

  alias world hello
end

Foo.new.hello #=&gt; "Hello"
Foo.new.world #=&gt; "Hello"

看下面这个例子:

class Foo
  def hello
    "Hello"
  end

   alias world hello

   def world
     "World"
   end

end

Foo.new.hello #=&gt; "Hello"
Foo.new.world #=&gt; "World"

ok,下面假设我们要覆写ActiveRecord::Base的find方法,使之接受新的option。假设我们要传入:second,返回第二个record。
这个时候alias_method_chain就派上用场了。

class Foo < ActiveRecord::Base
class << self

  #先将find方法别名
  alias find_without_second find

  #再定义find second方法
  def find_with_second(*args)
    ......something
    p "调用find之前"
   # 调用find_without_second 其实就是find方法
   find_without_second
    p "调用find之后"
  end
  alias find find_with_second
end
end
# 看看发生了什么
Foo.find(:second)
#实际调用find_with_second,在find_with_second方法中,
#又再调用了find_without_second,也就是原生的find方法了。

alias_method_chain 其实是一个DSL,合并了两次alias。

alias_method_chain :find, :second
#相当于
alias find_with_second find
alias find find_without_second

但是,alias_method_chain有点问题,

alias_method_chain limits extensibility
在rails3 中已经不推荐使用了。

下面我们看看在rails3中如何用super关键字来实现上诉功能

class Foo < ActiveRecord::Base  
  module FindWithSecond    
    def find(*args)      
      p "处理second参数"      
      super    
     end
 end
include FindWithSecond end
# 测试下
Foo.new.find(:second) #=> "处理second参数"

可以看到找到的是FindWithSecond中的find的方法,处理完后,通过super再找到父类的find方法。这里其实是用到了ruby的一个特性,就是include模块的先后次序,后include的模块会当成子类来看待。

class Foo

module First
end

module Second
end

include Second
include First
end

#看看Foo的祖先
Foo.ancestors #=> [Foo, Foo::First, Foo::Second, Object, Kernel]
#先include的Second当作了First的父类了