`
hideto
  • 浏览: 2666608 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Rails源码研究之ActiveRecord:五,Callbacks

    博客分类:
  • Ruby
阅读更多
Callbacks相关的源码在callbacks.rb文件里:
module ActiveRecord
  module Callbacks
    CALLBACKS = %w(
      after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
      after_validation before_validation_on_create after_validation_on_create before_validation_on_update
      after_validation_on_update before_destroy after_destroy
    )

    def self.included(base) #:nodoc:
      base.extend(ClassMethods)
      base.class_eval do
        class << self
          include Observable
          alias_method_chain :instantiate, :callbacks
        end

        [:initialize, :create_or_update, :valid?, :create, :update, :destroy].each do |method|
          alias_method_chain method, :callbacks
        end
      end

      CALLBACKS.each do |method|
        base.class_eval <<-"end_eval"
          def self.#{method}(*callbacks, &block)
            callbacks << block if block_given?
            write_inheritable_array(#{method.to_sym.inspect}, callbacks)
          end
        end_eval
      end
    end

    module ClassMethods #:nodoc:
      def instantiate_with_callbacks(record)
        object = instantiate_without_callbacks(record)

        if object.respond_to_without_attributes?(:after_find)
          object.send(:callback, :after_find)
        end

        if object.respond_to_without_attributes?(:after_initialize)
          object.send(:callback, :after_initialize)
        end

        object
      end
    end

    def create_or_update_with_callbacks #:nodoc:
      return false if callback(:before_save) == false
      result = create_or_update_without_callbacks
      callback(:after_save)
      result
    end

    def create_with_callbacks #:nodoc:
      return false if callback(:before_create) == false
      result = create_without_callbacks
      callback(:after_create)
      result
    end

    def update_with_callbacks #:nodoc:
      return false if callback(:before_update) == false
      result = update_without_callbacks
      callback(:after_update)
      result
    end

    def valid_with_callbacks? #:nodoc:
      return false if callback(:before_validation) == false
      if new_record? then result = callback(:before_validation_on_create) else result = callback(:before_validation_on_update) end
      return false if result == false

      result = valid_without_callbacks?

      callback(:after_validation)
      if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end

      return result
    end

    def destroy_with_callbacks #:nodoc:
      return false if callback(:before_destroy) == false
      result = destroy_without_callbacks
      callback(:after_destroy)
      result
    end

    private
      def callback(method)
        notify(method)

        callbacks_for(method).each do |callback|
          result = case callback
            when Symbol
              self.send(callback)
            when String
              eval(callback, binding)
            when Proc, Method
              callback.call(self)
            else
              if callback.respond_to?(method)
                callback.send(method, self)
              else
                raise ActiveRecordError, "Callbacks must be a symbol denoting the method to call, a string to be evaluated, a block to be invoked, or an object responding to the callback method."
              end
          end
          return false if result == false
        end

        result = send(method) if respond_to_without_attributes?(method)

        return result
      end

      def callbacks_for(method)
        self.class.read_inheritable_attribute(method.to_sym) or []
      end

  end
end


从源码中我们可以学习如下几点:
1,有四种类型的callback macros:
1)Method references(symbol)
class Topic < ActiveRecord::Base
  before_destroy :destroy_author
end

2)Callback objects
class BankAccount < ActiveRecord::Base
  before_save      EncryptionWrapper.new("credit_card_number")
  after_save       EncryptionWrapper.new("credit_card_number")
  after_initialize EncryptionWrapper.new("credit_card_number")
end

class EncryptionWrapper
  def initialize(attribute)
    @attribute = attribute
  end

  def before_save(record)
    record.credit_card_number = encrypt(record.credit_card_number)
  end

  def after_save(record)
    record.credit_card_number = decrypt(record.credit_card_number)
  end

  alias_method :after_find, :after_save

  private
    def encrypt(value)
      # Secrecy is committed
    end

    def decrypt(value)
      # Secrecy is unveiled
    end
end

3)Inline methods(proc)
class Firm < ActiveRecord::Base
  # Destroys the associated clients and people when the firm is destroyed
  before_destroy { |record| Person.destroy_all "firm_id = #{record.id}"   }
  before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
end

4)Inline eval methods(string)
class Topic < ActiveRecord::Base
  before_destroy 'self.class.delete_all "parent_id = #{id}"'
end


2,执行Model的如下方法时会调用callbacks:
instantiate(find)、initialize、create_or_update、valid?、create、update、destroy

3,activesupport\lib\activesupport\core_text\module\aliasing.rb:
class Module
  def alias_method_chain(target, feature)
    aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
    yield(aliased_target, punctuation) if block_given?
    alias_method "#{aliased_target}_without_#{feature}#{punctuation}", target
    alias_method target, "#{aliased_target}_with_#{feature}#{punctuation}"
  end
end

比如alias_method_chain :destroy, :callbacks的效果是调用destroy方法时会调用destroy_with_callbacks
而destroy_with_callbacks方法先调用callback(:before_destroy),然后destroy_without_callbacks(又被alias为destroy),然后callback(:after_destroy)
然后callback(:before_destroy)和callback(:after_destroy)分别调用before_destroy和after_destroy后面的macros
ActiveRecord的Callback就像一个Around_filter,实现了aop拦截的功能
分享到:
评论
1 楼 sina2009 2007-06-23  
学习了1

相关推荐

    ROR绿色最新环境(2013/3/10)

    Rails::Rack::Logger ActionDispatch::ShowExceptions ActionDispatch::DebugExceptions ActionDispatch::RemoteIp ActionDispatch::Reloader ActionDispatch::Callbacks ActiveRecord::ConnectionAdapters::...

    rails_async_migrations:对ActiveRecord :: Migration的异步支持

    RailsAsyncMigrations ActiveRecord::Migration扩展程序以一种简单直接的方式使您的迁移异步。动机创建该库的目的是为了帮助在技术水平上难以扩展的小型公司。 小型项目不需要异步迁移队列,大公司在遇到扩展问题时...

    Rails的精简版本Rails::API.zip

    Rails::API 是 Rails 的精简版本,针对不需要使用完整 Rails 功能的开发者。 Rails::API 移除了 ActionView 和其他一些渲染功能,不关心Web前端的开发者可更容易、快速地开发应用程序,因此运行速度比正常的 Rails ...

    userstamp, 这个 Rails 插件扩展ActiveRecord.zip

    userstamp, 这个 Rails 插件扩展ActiveRecord Userstamp插件( v-2.0 )概述Userstamp插件扩展了 ActiveRecord::Base,以添加对'创建者','更新程序'和'deleter'属性的自动更新。 它是基于 ActiveRecord::Timesta

    to_xls-rails:将Rails ActiveRecord或Mongid数据导出到Excel文件

    数组元素支持对象:ActiveRecord,Mongid,哈希。 在您的Gemfile中: gem 'to_xls-rails' # Last officially released gem # gem "to_xls-rails", :git =&gt; "git://github....

    ActiveRecord简单实例_activerecord.zip

    在Ruby on Rails框架中,ActiveRecord是一个至关重要的组件,它负责模型(Model)与数据库之间的交互。本实例将深入探讨ActiveRecord的基本用法,帮助理解如何在实际开发中有效地运用这个强大的工具。 首先,让我们...

    stateful_enum:基于ActiveRecord :: Enum构建的非常简单的状态机插件

    stateful_enum是建立在ActiveRecord的内置ActiveRecord :: Enum之上的状态机gem。 安装 将此行添加到您的Rails应用程序的Gemfile中: gem 'stateful_enum' 和捆绑。 动机 您不需要抽象 stateful_enum取决于...

    ActiveRecord-Without-Rails:只是在没有Rails的情况下使用ActiveRecord迁移的简单示例

    没有Rails的ActiveRecord 只是在没有Rails的情况下使用ActiveRecord迁移的简单示例您可以执行的任务: rake db:create rake db:migrate rake db:dropRails 5+的注意事项请注意,即使使用Rails 5,您也需要rake db:...

    枚举:具有I18n和ActiveRecordMongoid支持的枚举属性

    Rails 5.2+用法基本: class User extend Enumerize enumerize :sex , in : [ :male , :female ]end 请注意,枚举值仅是标识符,因此,如果要使用多字等值,则应使用I18n功能。 ActiveRecord: class CreateUsers &...

    Ruby on Rails中的ActiveRecord编程指南

    ### Ruby on Rails中的ActiveRecord编程指南 #### 一、引言 在Ruby on Rails框架中,ActiveRecord是一种用于实现数据库抽象层的对象关系映射(ORM)工具。它为开发人员提供了一种简单而强大的方式来处理数据库记录...

    Rails项目源代码

    这个Rails项目提供了学习和研究Web开发的机会,特别是对于Ruby on Rails新手,可以通过阅读和理解源代码来提升技能,了解实际应用中Rails的用法。同时,对于有经验的开发者,这个项目也可以作为一个起点,进行二次...

    paranoid2:Rails 4 的偏执模型

    Rails 4 定义了ActiveRecord::Base#destroy! 所以Paranoid2 gem 使用force: true arg 来强制销毁。 安装 将此行添加到应用程序的 Gemfile 中: gem 'paranoid2' 然后执行: $ bundle 用法 将deleted_at: ...

    hashid-rails:在Rails应用程序ActiveRecord模型中使用Hashids(http:hashids.orgruby)

    在要启用哈希值的ActiveRecord模型中包括Hashid Rails。 class Model &lt; ActiveRecord :: Base include Hashid :: Rails end 继续使用Model#find输入hashid或常规的'ol id。 @person = Person . find ( params...

    rails-exporter-源码.rar

    《Rails Exporter 源码解析》 Rails Exporter 是一个用于 Rails 应用程序的开源工具,主要用于数据导出功能。源码分析将帮助我们深入理解其内部工作原理,以便更好地利用它来优化我们的应用。 一、Rails 框架基础 ...

    workflow-activerecord:工作流库的 ActiveRecordRails 集成

    工作流库的 ActiveRecord/Rails 集成 工作流活动记录的主要+次要版本基于最旧的兼容 ActiveRecord API。 要在 Rails/ActiveRecord 4.1、4.2、5.0、5.1、5.2、6.0、6.1 中使用 ,请使用: gem 'workflow-...

    flowdock-rails:用于 ActiveRecord 模型的 Flowdock 通知程序

    Flowdock::Rails 这个 gem 添加了一个类方法来向特定流发送通知,以在启用的资源上创建和更新事件安装将此行添加到应用程序的 Gemfile 中: gem 'flowdock-rails'然后执行: $ bundle或者自己安装: $ gem install ...

    Ruby on Rails入门例子

    - **ActiveRecord查询接口(Query Interface)**:ActiveRecord提供了丰富的查询API,如`User.find(1)`, `Post.where(title: 'Hello')`,用于从数据库检索数据。 - ** erb语法**:在视图文件中,我们可以使用erb...

    Ruby on Rails入门经典代码

    Ruby on Rails,简称Rails,是基于Ruby语言的一个开源Web应用程序框架,它遵循MVC(Model-View-Controller)架构模式,旨在使Web开发过程更加高效、简洁。本压缩包中的"Ruby on Rails入门经典代码"提供了新手学习...

    Apress Pro ActiveRecord Databases with Ruby and Rails.pdf

    1. **自动化的表映射**:ActiveRecord可以根据类名自动推断出对应的数据库表名,并根据类属性推断出表中的列。 2. **数据验证**:提供了丰富的验证规则,如唯一性验证、存在性验证等,确保数据的完整性和一致性。 3....

    Rails上的API:使用Rails构建REST APIAPIs on Rails: Building REST APIs with Rails

    1. **快速开发**:Rails内置了许多实用的功能和库,如ActiveRecord ORM、MVC架构等,这些都能够极大地加快开发进度。 2. **代码简洁**:Rails遵循“约定优于配置”的原则,这意味着开发者无需编写大量重复代码就能...

Global site tag (gtag.js) - Google Analytics