美文网首页
Active Record的设计

Active Record的设计

作者: will2yang | 来源:发表于2019-11-05 22:52 被阅读0次
    自动加载机制
    require "active_support"
    require "active_support/rails"
    require "active_model"
    require "arel"
    require "yaml"
    
    require "active_record/version"
    require "active_model/attribute_set"
    
    module ActiveRecord
      extend ActiveSupport::Autoload
    
      autoload :Base
      autoload :Callbacks
      autoload :Core
      autoload :ConnectionHandling
      autoload :CounterCache
      autoload :DynamicMatchers
      autoload :Enum
      autoload :InternalMetadata
      autoload :Explain
      autoload :Inheritance
      autoload :Integration
      autoload :Migration
      autoload :Migrator, "active_record/migration"
      autoload :ModelSchema
      autoload :NestedAttributes
      autoload :NoTouching
      autoload :TouchLater
      autoload :Persistence
      autoload :QueryCache
      autoload :Querying
      autoload :CollectionCacheKey
      autoload :ReadonlyAttributes
      autoload :RecordInvalid, "active_record/validations"
      autoload :Reflection
      autoload :RuntimeRegistry
      autoload :Sanitization
      autoload :Schema
      autoload :SchemaDumper
      autoload :SchemaMigration
      autoload :Scoping
      autoload :Serialization
      autoload :StatementCache
      autoload :Store
      autoload :Suppressor
      autoload :Timestamp
      autoload :Transactions
      autoload :Translation
      autoload :Validations
      autoload :SecureToken
    
      eager_autoload do
        autoload :ActiveRecordError, "active_record/errors"
        autoload :ConnectionNotEstablished, "active_record/errors"
        autoload :ConnectionAdapters, "active_record/connection_adapters/abstract_adapter"
    
        autoload :Aggregations
        autoload :Associations
        autoload :AttributeAssignment
        autoload :AttributeMethods
        autoload :AutosaveAssociation
    
        autoload :LegacyYamlAdapter
    
        autoload :Relation
        autoload :AssociationRelation
        autoload :NullRelation
    
        autoload_under "relation" do
          autoload :QueryMethods
          autoload :FinderMethods
          autoload :Calculations
          autoload :PredicateBuilder
          autoload :SpawnMethods
          autoload :Batches
          autoload :Delegation
        end
    
        autoload :Result
        autoload :TableMetadata
        autoload :Type
      end
    
      module Coders
        autoload :YAMLColumn, "active_record/coders/yaml_column"
        autoload :JSON, "active_record/coders/json"
      end
    
      module AttributeMethods
        extend ActiveSupport::Autoload
    
        eager_autoload do
          autoload :BeforeTypeCast
          autoload :Dirty
          autoload :PrimaryKey
          autoload :Query
          autoload :Read
          autoload :TimeZoneConversion
          autoload :Write
          autoload :Serialization
        end
      end
    
      module Locking
        extend ActiveSupport::Autoload
    
        eager_autoload do
          autoload :Optimistic
          autoload :Pessimistic
        end
      end
    
      module ConnectionAdapters
        extend ActiveSupport::Autoload
    
        eager_autoload do
          autoload :AbstractAdapter
        end
      end
    
      module Scoping
        extend ActiveSupport::Autoload
    
        eager_autoload do
          autoload :Named
          autoload :Default
        end
      end
    
      module Tasks
        extend ActiveSupport::Autoload
    
        autoload :DatabaseTasks
        autoload :SQLiteDatabaseTasks, "active_record/tasks/sqlite_database_tasks"
        autoload :MySQLDatabaseTasks,  "active_record/tasks/mysql_database_tasks"
        autoload :PostgreSQLDatabaseTasks,
          "active_record/tasks/postgresql_database_tasks"
      end
    
      autoload :TestFixtures, "active_record/fixtures"
    
      def self.eager_load!
        super
        ActiveRecord::Locking.eager_load!
        ActiveRecord::Scoping.eager_load!
        ActiveRecord::Associations.eager_load!
        ActiveRecord::AttributeMethods.eager_load!
        ActiveRecord::ConnectionAdapters.eager_load!
      end
    end
    
    ActiveSupport.on_load(:active_record) do
      Arel::Table.engine = self
    end
    
    ActiveSupport.on_load(:i18n) do
      I18n.load_path << File.expand_path("active_record/locale/en.yml", __dir__)
    end
    
    YAML.load_tags["!ruby/object:ActiveRecord::AttributeSet"] = "ActiveModel::AttributeSet"
    YAML.load_tags["!ruby/object:ActiveRecord::Attribute::FromDatabase"] = "ActiveModel::Attribute::FromDatabase"
    YAML.load_tags["!ruby/object:ActiveRecord::LazyAttributeHash"] = "ActiveModel::LazyAttributeHash"
    

    Active Record是Rails的ORM功能实现。上面代码使用了ActiveSupport::Autoload模块,该模块定义了autoload方法。代码首次引用模块时,这个方法通过命名约定自动识别和加载该模块。
    我们先看下Autoload模块代码的具体实现:

    # frozen_string_literal: true
    
    require "active_support/inflector/methods"
    
    module ActiveSupport
      # Autoload and eager load conveniences for your library.
      #
      # This module allows you to define autoloads based on
      # Rails conventions (i.e. no need to define the path
      # it is automatically guessed based on the filename)
      # and also define a set of constants that needs to be
      # eager loaded:
      #
      #   module MyLib
      #     extend ActiveSupport::Autoload
      #
      #     autoload :Model
      #
      #     eager_autoload do
      #       autoload :Cache
      #     end
      #   end
      #
      # Then your library can be eager loaded by simply calling:
      #
      #   MyLib.eager_load!
      module Autoload
        def self.extended(base) # :nodoc:
          base.class_eval do
            @_autoloads = {}
            @_under_path = nil
            @_at_path = nil
            @_eager_autoload = false
          end
        end
    
        def autoload(const_name, path = @_at_path)
          unless path
            full = [name, @_under_path, const_name.to_s].compact.join("::")
            path = Inflector.underscore(full)
          end
    
          if @_eager_autoload
            @_autoloads[const_name] = path
          end
    
          super const_name, path
        end
    
        def autoload_under(path)
          @_under_path, old_path = path, @_under_path
          yield
        ensure
          @_under_path = old_path
        end
    
        def autoload_at(path)
          @_at_path, old_path = path, @_at_path
          yield
        ensure
          @_at_path = old_path
        end
    
        def eager_autoload
          old_eager, @_eager_autoload = @_eager_autoload, true
          yield
        ensure
          @_eager_autoload = old_eager
        end
    
        def eager_load!
          @_autoloads.each_value { |file| require file }
        end
    
        def autoloads
          @_autoloads
        end
      end
    end
    

    首先看到autoload方法,如果再没有传path参数的情况下,autoload会根据当前的类(模块)名,和传入的const_name,组成一个path,最后根据传入的const_name,和path,调用ruby原生的autoload加载模块。如果通过传递block的方式调用eager_autoload,这种情况下调用autoload,会把相应的模块加入到@_eager_autoload中,然后可以通过eager_load!方法直接require对应path下的文件。以及相应的autoload_at和autoload_under,也可以通过block的方式传递path,改变默认的@_at_path,执行autoload,真的是非常巧妙的方式。
    ruby原生的autoload部分,参考:https://www.jianshu.com/p/d9dcbed59a82

    Validations模块

    ActiveRecord::Base包含了 ActiveRecord::Validations模块,里面却找不到我们要用的validate方法。这个模块来自Active Model,Active Record的一个依赖库。为什么作者要把validate方法定义在别的库呢,其实早期的rails没有Active Model库,那时候validate方法定义在Active Record里,但是随着Active Record的不断壮大,开发者发现其实这是两项独立的工作,一项与数据库操作相关,比如保存和加载数据库。但另一项是处理对象模型的,比如维护对象属性,或者跟踪对象属性的有效性。于是就被分成了两个库。

    相关文章

      网友评论

          本文标题:Active Record的设计

          本文链接:https://www.haomeiwen.com/subject/lzgebctx.html