ActiveSupport autoload源码分析

6 篇文章 0 订阅

在我们平常看一些gem或者开发中autoload用的还是很多的,闲的没事总结下这个方法的内部实现,分析一下源码。

在我们看源码的时候经常看到如下代码:

module ActiveRecord
  extend ActiveSupport::Autoload

  autoload :Attribute
  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到底做了什么事??

看源码:

   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

我们来分析下这个方法取上面的ActiveRecord来分析:

第一步:

extend ActiveSupport::Autoload

这里相当把ActiveSupport::Autoload的方法引入进来了,相当于类方法,具体细节看extend的用法,不多说,好,继续向下看。

第二步:

autoload :Attribute

这里相当于调用了

ActiveRecord.autoload(:Attribute)

再来细看这个方法,这个方法接入了两个参数通过上面分析我们知道:

const_name=:Attribute
path = @_at_path = nil

这里的@_at_path是extend钩子方法extended调用初始化的值,继续向下看:

 unless path
        full = [name, @_under_path, const_name.to_s].compact.join("::")
        path = Inflector.underscore(full)
      end

path=nil这个方法会执行,我们先来确认几个值。

name = self.name = ActiveRecord.name = "ActiveRecord"
@_under_path = nil
const_name.to_s = :Attribute.to_s = "Attribute"

好了我么知道这几个值那就简单了,把这几个值带到full里面执行下

full =  ["ActiveRecord", nil, "Attribute"].compact.join("::") = "ActiveRecord::Attribute"

继续向下看:

path = Inflector.underscore(full)

其实是调用了Inflector里面的underscore方法只是把full传进来了,看源码:

    def underscore(camel_cased_word)
      return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/
      word = camel_cased_word.to_s.gsub('::'.freeze, '/'.freeze)
      word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }#{$2.downcase}" }
      word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
      word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
      word.tr!("-".freeze, "_".freeze)
      word.downcase!
      word
    end

这个方法很长,不具体分析了,其实这个方法具体就做了一件事,就是把我们的full转化为我们需要的路径。
上面的

full = "ActiveRecord::Attribute"

经过这个方法转化后

"active_record/attribute"

继续向下看:

  if @_eager_autoload
        @_autoloads[const_name] = path
      end

      super const_name, path
  end

@_eager_autoload = nil 所以最后只执行了:

super const_name, path

相当于:

super :Attribute, ""active_record/attribute""

其实分析到这里已经结束了,这里的super相当于调用了kernel下的autoload方法,这个是ruby的方法,执行这个方法成功返回nil,失败异常,简单提一下,其实就是懒加载,调用这个方法其实并没有require,只是把路径加到内存中了,当我们调用的时候回才去加载。

下面还需要提出一个小问题:为什么rails重写了ruby的这个方法而不是直接调用?

其实这个方法的目的就要还是为了Rails的约束,比如
A::B => rails 当然根据这个名字找到a/b文件,所以重写了这个方法
但是ruby不是这样的,没有这个规范,只要有路径就能找到,不符合rails的约束,人家当然要重写了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值