update_attribute和update_attributes都是用来修改model的属性,它们区别除了一个修改单个属性,一个修改多个属性外,最重要的是update_attribute不执行validation,而update_attributes执行validation,查看源码:

def update_attribute(name, value)
  send(name.to_s + '=', value)
  save(false)
end
def update_attributes(attributes)
  self.attributes = attributes
  save
end

可以看出来,update_attribute执行的是save(false),而update_attributes执行的是save

Rails2.3.3新功能──touch

2009-07-29 22:54:28 +0800

touch是Rails2.3.3引入的新功能,可以将指定的attributes改为当前时间,默认是更改updated_at或updated_on。

典型的用法在many-to-one时,当many端发生改变时,更新one端的updated_at时间。比如在一个论坛系统中,一个帖子的更新时间会随着之后的回复发生改变:

class Post < ActiveRecord::Base
  has_many :replies
end
class Reply < ActiveRecord::Base
  belongs_to :post, :touch => true
end

这里声明的:touch => true,其实就是定义了一个method来更新Post的updated_at时间,并且在after_save和after_destroy的时候调用该method

def add_touch_callbacks(reflection, touch_attribute)
  method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym
  define_method(method_name) do
    association = send(reflection.name)

    if touch_attribute == true
      association.touch unless association.nil?
    else
      association.touch(touch_attribute) unless association.nil?
    end
  end
  after_save(method_name)
  after_destroy(method_name)
end

 

Rails源码分析——delegate

2009-04-13 23:55:10 +0800

Delegate是一种应用composite来代替extend的机制,可以有效地降低代码的耦合性。

Rails 2.2增加了delegate方法,可以十分方便地实现delegate机制。来看看源码吧:

def delegate(*methods)  
  options = methods.pop  
  unless options.is_a?(Hash) && to = options[:to]  
    raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."  
  end  
   
  if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/  
    raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."  
  end  
   
  prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"  
  
  methods.each do |method|  
    module_eval(<<-EOS, "(__DELEGATION__)", 1)  
      def #{prefix}#{method}(*args, &block)  
        #{to}.__send__(#{method.inspect}, *args, &block)  
      end  
    EOS  
  end  
end  

delegate方法首先检查传入的参数,正确参数形式为:method1, :method2, ..., :methodN, :to => klass[, :prefix => prefix]

delegate要求参数的最后必须是一个Hash,:to表示需要代理的类,:prefix表示代理的方法是否要加前缀,如果:prefix => true,则代理的方法名为klass_method1, klass_method2, ..., klass_methodN,如果:prefix => prefix (prefix为string),则代理的方法名为prefix_method1, prefix_method2, ..., prefix_methodN。

最终通过module_eval动态生成每个方法定义。通过__send__方法调用:to类的方法。

来看看调用的例子:

简单的调用:

class Greeter < ActiveRecord::Base  
  def hello()   "hello"   end  
  def goodbye() "goodbye" end  
end  
   
class Foo < ActiveRecord::Base  
  delegate :hello, :goodbye, :to => :greeter  
end  
   
Foo.new.hello   # => "hello"  
Foo.new.goodbye # => "goodbye"  

增加:prefix => true:

class Foo < ActiveRecord::Base  
  delegate :hello, :goodbye, :to => :greeter, :prefix => true  
end  
   
Foo.new.greeter_hello   # => "hello"  
Foo.new.greeter_goodbye # => "goodbye"  

自定义前缀名:

class Foo < ActiveRecord::Base  
  delegate :hello, :goodbye, :to => :greeter, :prefix => :foo  
end  
  
Foo.new.foo_hello   # => "hello"  
Foo.new.foo_goodbye # => "goodbye"  

ruby的动态性再一次发挥了强大的功能!