`
fantaxy025025
  • 浏览: 1329324 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

Rails源码阅读(三)Rails::Initializer

 
阅读更多

 

启动的落脚点

不论启动console还是启动server,最终都需要加载rails环境,这个落脚点在environment.rb中的Rails::Initializer.run 

Rails::Initializer.run do |config|

。。。

end
 

启动前的配置Configuration

加载rails环境,需要的配置是比较多的,例如:启动的root_path,启动时要加载的frameworks,加载的路径,插件的位置,数据库的配置,log的配置,i18n的配置等等。这些都在Configuration类中配置。

  # The Configuration class holds all the parameters for the Initializer and
  # ships with defaults that suites most Rails applications. But it's possible
  # to overwrite everything. Usually, you'll create an Configuration file
  # implicitly through the block running on the Initializer, but it's also
  # possible to create the Configuration instance in advance and pass it in
  # like this:
  #
  #   config = Rails::Configuration.new
  #   Rails::Initializer.run(:process, config)
  class Configuration

  。。。

  end
  

启动前再配置

    def self.run(command = :process, configuration = Configuration.new)
      yield configuration if block_given?
      initializer = new configuration
      initializer.send(command)
      initializer
    end

# 在上面environment的启动中,configuration参数使用了默认的配置值Configuration.new

# 同时看到了,yield configuration if block_given?会把configuration传递给block,这样可以在启动时配置config的一些参数,而不用修改configuration类(这样也是可以的)

# 再回到上面的落脚点的代码,看到了这里的参数config原来就是configuration,也就是说想知道怎么配置config,需要了解configuration的内容和api。

好了,好多人跟我一样,困惑许久的config是什么,如何配置的问题,现在解决了。

 

启动1

继续往下看,这里有一句:initializer = new configuration

这种写法让我这种多年的javaer很反感,明明这么写更好理解么:initializer = Initializer.new(configuration)

javaer理解中的new是这样的:User user = new User(...)

initializer.send(command)

 

 

 

这里调用了实例方法command,根据command = :process知道,调用的方法是process方法。就是这里进入启动环节。

 

启动2 可以说,直接看这里就是rails的启动过程

    # Sequentially step through all of the available initialization routines,
    # in order (view execution order in source).
    def process

。。。

    end
 

 

启动3 仔细看看启动都做了些什么

check_ruby_version

检查版本,不对的话会报异常,详细略

 

install_gem_spec_stubs

看看stub的字样,表示这里的install其实是个“伪”install,目的是给plugin使用的。

说明:

  This allows Gem plugins to depend on Rails even when the Gem version of Rails shouldn't be loaded

代码:

  install_gem_spec_stubsGem.loaded_specs[stub] = Gem::Specification.new 。。。

 

set_load_path

配置$LOAD_PATH

说明:Set the $LOAD_PATH based on the value of Configuration

 

add_gem_load_paths

使用的对象为@gems

  来源:configuration中的@gems << Rails::GemDependency.new(name, options)

调用了Rails::GemDependency的add_load_paths方法,这个方法内容主要使用了gem ‘some_gem' 'edition_n'

 

require_frameworks

依次执行 require ‘activesupport’ 等, 简而言之,这里才真正的require了,这里之后才真正可以使用这些framework

这里之后的方法,基本可以说能使用rails的这些组件了

 

set_autoload_paths

字面意义

把configuration里面的auto_load_paths 赋值给了ActiveSupport::Dependencies

关于ActiveSupport::Dependencies,以后再看看,暂不清楚内部原理

 

add_plugin_load_paths

主要由plugin_loader来

 

load_environment

这里会调用到所在环境(production,development,test)对应的rb文件

    def environment_path
      "#{root_path}/config/environments/#{environment}.rb"
    end

 并且把常量设置在了Object中(好处呢,首先是在任何地方都可以直接用了吧)

        (self.class.constants - constants).each do |const|
          Object.const_set(const, self.class.const_get(const))
        end

 我在我的环境里看了看,果然常量增加了不少

 

#irb初始情况下:
irb(main):011:0>  Object.constants.size
=> 101

#我的rails环境
?> Object.constants.size
=> 429

最后再重复一个问题,程序运行到这里,能不能使用rails的插件呢?

可以!例如active_support

但不全部,例如有些组建还要进行初始化操作,像active_record的数据库建立等

 

preload_frameworks

作用是为了Passenger/JRuby等使用

主要操作是显式调用各个组件的 framework.load_all! 方法

我看了看几个组件,居然是都有load_all!这个方法

module ActiveSupport
  def self.load_all!
    [Dependencies, Deprecation, Gzip, MessageVerifier, Multibyte, SecureRandom, TimeWithZone]
  end
。。。
end

module ActiveRecord
  # TODO: Review explicit loads to see if they will automatically be handled by the initilizer.
  def self.load_all!
    [Base, DynamicFinderMatch, ConnectionAdapters::AbstractAdapter]
  end
。。。
end
 

initialize_encoding

这个类做了什么呢,怎么设置编码的呢?

Ruby1.8设置$KCODE

Ruby1.9默认是utf-8了

# For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the
    # multibyte safe operations. Plugin authors supporting other encodings
    # should override this behaviour and set the relevant +default_charset+
    # on ActionController::Base.
    #
    # For Ruby 1.9, this does nothing. Specify the default encoding in the Ruby
    # shebang line if you don't want UTF-8.
    def initialize_encoding
      $KCODE='u' if RUBY_VERSION < '1.9'
    end
 

 

initialize_database

这里才建立数据库连接了!!

也就是说从这里开始才能使用ActiveRecord来查询数据库

 

initialize_cache

放置缓存,页面缓存,块缓存等的地方

*1 如果默认位置: "#{root_path}/tmp/cache/"路径不存在则用内存了

    def default_cache_store
        if File.exist?("#{root_path}/tmp/cache/")
          [ :file_store, "#{root_path}/tmp/cache/" ]
        else
          :memory_store
        end
      end

*2 这里从config得到了cache_store的类型,并初始化了一个cache_store

     这里居然用了silence_wornings,很明显不让你随意设置咯

silence_warnings { Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(configuration.cache_store) }

 

*3 cache_store的结果是返回一个cache_store对象,并不是一个配置

    # Creates a new CacheStore object according to the given options.
    #
    # If no arguments are passed to this method, then a new
    # ActiveSupport::Cache::MemoryStore object will be returned.
    #
    # If you pass a Symbol as the first argument, then a corresponding cache
    # store class under the ActiveSupport::Cache namespace will be created.
    # For example:
    #
    #   ActiveSupport::Cache.lookup_store(:memory_store)
 

initialize_framework_caches

这里只是简单的使用了上面设置的cache

 

    def initialize_framework_caches
      if configuration.frameworks.include?(:action_controller)
        ActionController::Base.cache_store ||= RAILS_CACHE
      end
    end

 

 

initialize_logger

initialize_framework_logging

设置logger,如果config里面设置了logger,则基本跳过了。

*1 logger用的哪个类呢

     logger = ActiveSupport::BufferedLogger.new(configuration.log_path)

*2 production的特殊配置

          if configuration.environment == "production"
            logger.auto_flushing = false
          end  

 

*3 logger leve 这里是默认的,可以在config里配置新的

 

      def default_log_path
        File.join(root_path, 'log', "#{environment}.log")
      end

      def default_log_level
        environment == 'production' ? :info : :debug
      end

*4 可以给不同的组建单独配置其logger

     这个在ide测试的时候有用,比如在test里,可以把active_record的log打入ide,便于调试

    def initialize_framework_logging
      for framework in ([ :active_record, :action_controller, :action_mailer ] & configuration.frameworks)
        framework.to_s.camelize.constantize.const_get("Base").logger ||= Rails.logger
      end

      ActiveSupport::Dependencies.logger ||= Rails.logger
      Rails.cache.logger ||= Rails.logger
    end
 

initialize_dependency_mechanism

代理了类加载的机制,从而可以配置在开发环境和product环境中,加载类可以用不同的机制

 

    # Sets the dependency loading mechanism based on the value of
    # Configuration#cache_classes.
    def initialize_dependency_mechanism
      ActiveSupport::Dependencies.mechanism = configuration.cache_classes ? :require : :load
    end

 

 ActiveSupport::Dependencies这个类显然有很重要的地位,比如你看看大部分你用到的类都没有使用require ‘xxx’方法!!

 

 

initialize_whiny_nils

不用多说了,就是whiny_nil 的改写

 

initialize_time_zone

这个意图也清楚。但下面的代码没有弄明白具体含义,待补充吧。

设置adtive_record可以理解,但是最后一行,么意思呢。。。???

 

        if configuration.frameworks.include?(:active_record)
          ActiveRecord::Base.time_zone_aware_attributes = true
          ActiveRecord::Base.default_timezone = :utc
        end

 

 

initialize_i18n

    def initialize_i18n
      configuration.i18n.each do |setting, value|
        if setting == :load_path
          I18n.load_path += value
        else
          I18n.send("#{setting}=", value)
        end
      end
    end

  #1 可见,如果配置了load_path则I18n类中加入这个路径;否则,认为是配置i18n的属性

 

  #2 在config中可以这么配置:

  # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
  # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')]
  #   config.i18n.default_locale = :cn
  config.i18n.default_locale = 'zh_cn'

  config.i18n.load_path += Dir[
    Rails.root.join('config', 'locales', 'user', '*.{rb,yml}'),
    Rails.root.join('config', 'locales', 'message', '*.{rb,yml}')
  ]

  #3 最后,必须要说的是,这里用到了重写的ActiveSupport的类:

class Rails::OrderedOptions < Array                。。。              end
 

initialize_framework_settings 

    # Initializes framework-specific settings for each of the loaded frameworks
    # (Configuration#frameworks). The available settings map to the accessors
    # on each of the corresponding Base classes.
    def initialize_framework_settings
      configuration.frameworks.each do |framework|
        base_class = framework.to_s.camelize.constantize.const_get("Base")

        configuration.send(framework).each do |setting, value|
          base_class.send("#{setting}=", value)
        end
      end
      configuration.active_support.each do |setting, value|
        ActiveSupport.send("#{setting}=", value)
      end
    end

 根据下面的注释,可以知道:

*1 某个framefork都有哪些项目可以配置?例如active_record

     参考framefork的Base类

*2 配置的地方?

     还是上面提到的,在environment的中配置

*3 配置怎么写?

# Show full error reports and disable caching
config.action_controller.consider_all_requests_local = true
config.action_view.debug_rjs                         = true

 #June: we can test cache here(comment 2 lines && uncomment 3 lines after)
config.action_controller.perform_caching             = false
config.action_view.cache_template_loading            = false

 *4 为什么可以这么写?

    还是需要明白Rails::OrderedOptions也就是ActiveSupport::OrderedOptions的用法

 *5 active_support应该没有base类咯,看出来了么

 

initialize_framework_views

基本上可以同上。

 

initialize_metal

请看metal的用法吧

 

add_support_load_paths

空方法???

 

check_for_unbuilt_gems

这里可以安装config中配置到gem

 

load_gems

使用config中的gem

 

 

      # pick up any gems that plugins depend on

      add_gem_load_paths

      load_gems

      check_gem_dependencies

 

 

load_application_initializers

config/initializers目录下面的文件会在rails server启动最后顺序执行,顺序为文件名排序,所以有依赖关系的话,就得提前安排一下,例如db:migrate那样加入时间戳么。总之在这里安排顺序就是个很懒的设计了。

看看源码:

    def load_application_initializers
      if gems_dependencies_loaded
        Dir["#{configuration.root_path}/config/initializers/**/*.rb"].sort.each do |initializer|
          load(initializer)
        end
      end
    end

 

方法没有叫做after_initialize,因为这里安排的仍然是app启动的一项

 

after_initialize 

这里才是启动之后的操作,回调。

例如这里可以给分页的插件中文话。

 

initialize_database_middleware

这里会检查配置的session_store情况,如果配置了下面的目标内容,就会用数据库来存session了

ActionController::Base.session_store.name == 'ActiveRecord::SessionStore' (去配置config)

 

prepare_dispatcher

# Prepare dispatcher callbacks and run 'prepare' callbacks

看样子好像是加载了dispatcher并define_dispatcher_callbacks(configuration.cache_classes)

这里设置了@@cache_classes = cache_classes

作用:

# In the development environment your application's code is reloaded on
# every request.  This slows down response time but is perfect for development
# since you don't have to restart the webserver when you make code changes.
config.cache_classes = false

 

initialize_routing

代码:

    # If Action Controller is not one of the loaded frameworks (Configuration#frameworks)
    # this does nothing. Otherwise, it loads the routing definitions and sets up
    # loading module used to lazily load controllers (Configuration#controller_paths).
    def initialize_routing
      return unless configuration.frameworks.include?(:action_controller)

      ActionController::Routing.controller_paths += configuration.controller_paths
      #### Me added this line ###
      ActionController::Routing::Routes.add_configuration_file(File.join("/home/lijg/workrubygit/t8", 'config', 'routes2.rb'))
      ActionController::Routing::Routes.add_configuration_file(configuration.routes_configuration_file)
      ActionController::Routing::Routes.reload!
    end

 

这里的作用:

*1 知晓controller的位置,等需要的时候去得到这个类

*2 知晓routes文件的位置,load it

     这里的文件,可以是多个的,内部是可以处理的

configuration_files.each { |config| load(config) }

*3 reload!

      def load!
        Routing.use_controllers!(nil) # Clear the controller cache so we may discover new ones
        clear!
        load_routes!
      end

      # reload! will always force a reload whereas load checks the timestamp first
      alias reload! load!

*4 加入一些自己想要的controller

有时候想测试一个需求(不用git哦),想加入一些自己的routes和mvc看看情况,这个时候就可以用这个了。

这里仅仅是个小实验,所以简单修改了下源码。

*5 注意这里的顺序,路由加入的时候,优先加入的会先匹配。这样我的测试路由优先加入比较好~

 

load_observers

这个是给active_record使用的ActiveRecord::Base.instantiate_observers

看看里面的代码:

"#{observer} must be a lowercase, underscored class name (or an instance of the class itself) responding to the instance method. Example: Person.observers = :big_brother # calls BigBrother.instance"

 

load_view_paths

这两个组件在用view的路径

ActionController::Base.view_paths.load!

ActionMailer::Base.view_paths.load!

 

load_application_classes

*1 如果设置了某些自动加载的路径,这时会加载

     例如在env中这样的设置:config.load_paths <<  "#{RAILS_ROOT}/app/middleware"

*2 加载了这个目录以及子目录下的所有.rb文件

     Dir.glob("#{load_path}/**/*.rb"

*3 这里加载方式:require_dependency

 

    # Eager load application classes
    def load_application_classes
      return if $rails_rake_task
      if configuration.cache_classes
        configuration.eager_load_paths.each do |load_path|
          matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/
          Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
            require_dependency file.sub(matcher, '\1')
          end
        end
      end
    end 

写到这里,我想需要把rails2的自动加载路径打印出来看看

 

--------------------------------------------------

/home/lijg/workrubygit/t8/app/controllers/

/home/lijg/workrubygit/t8/app

/home/lijg/workrubygit/t8/app/models

/home/lijg/workrubygit/t8/app/controllers

/home/lijg/workrubygit/t8/app/helpers

/home/lijg/workrubygit/t8/lib #看见了,这个在rails2是自动加入的,但rails3好像不同了

/home/lijg/workrubygit/t8/vendor

/home/lijg/workrubygit/t8/config/../vendor/rails/railties/lib/../builtin/rails_info/

/home/lijg/workrubygit/t8/app/middleware #这个是我自己设置的

--------------------------------------------------

 

disable_dependency_loading

用到了这个:ActiveSupport::Dependencies.unhook!

 

本篇完毕

作业:需要再仔细看看的

require_dependency

auto_load

ActiveSupport::Dependencies

 

----本篇完毕---- -----EOF---- ----THE END----

+

o

o

o

 

 

 

 

 

 

 

分享到:
评论
2 楼 fantaxy025025 2011-10-14  
orcl_zhang 写道
这个太简单了吧。。。

sorry阿,这里暂时占了个位置,还是没有那么多时间一下子写完了~待续~
1 楼 orcl_zhang 2011-10-14  
这个太简单了吧。。。

相关推荐

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

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

    Rails项目源代码

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

    rails-exporter-源码.rar

    三、源码结构分析 1. Models:源码中的模型文件可能包含了对数据库进行查询的逻辑,以获取需要导出的数据。Rails 的 Active Record 提供了强大的 ORM(对象关系映射),使得数据库操作变得简单。 2. Controllers:...

    ruby on rails 教程源码

    这个“ruby on rails 教程源码”很可能是为了辅助学习者深入理解Rails的工作原理和最佳实践,通过实际操作来提升技能。 在Rails中,`sample_app-master`可能是一个示例应用程序的主目录,它包含了完整的项目结构。...

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

    在本篇内容中,我们将深入探讨如何利用Ruby on Rails(简称Rails)这一强大的Web应用程序框架来构建可伸缩且易于维护的RESTful API。Rails以其简洁优雅的语法、高效的开发速度以及良好的社区支持而闻名,这使得它...

    Rails进行敏捷Web开发(所有版本的源码rails3.0-4.0)

    在“Rails进行敏捷Web开发(所有版本的源码rails3.0-4.0)”中,包含了Rails从3.0到4.0各个主要版本的源代码,这些版本的变迁反映了Rails框架在不同阶段的发展和改进。 1. Rails 3.0: Rails 3是重大升级,引入了...

    Rails中应用Ext.tree:以中国的省市地区三级联动选择为例

    在Ruby on Rails(Rails)框架中,开发人员经常需要实现各种用户交互功能,例如三级联动选择,这在处理如中国省市区这样的地理数据时尤其常见。这篇博客文章“Rails中应用Ext.tree:以中国的省市地区三级联动选择为...

    Ruby on Rails源代码

    Ruby on Rails(简称Rails)是一种基于Ruby编程语言的开源Web应用程序框架,它遵循MVC(模型-视图-控制器)架构模式,旨在提高开发效率和可读性。Rails的哲学强调“约定优于配置”和“Don't Repeat Yourself”(DRY...

    rails 的安装

    标题 "rails 的安装" 涉及到的是Ruby on Rails框架的安装过程,这是一个用于构建Web应用程序的开源框架。Rails是基于Ruby编程语言,它强调DRY(Don't Repeat Yourself)原则,提供了一种优雅的方式来组织和编写代码...

    基于ruby on rails开发示例源码

    1. **Gemfile**:列出项目依赖的Ruby gems(库),Rails和其他第三方库都通过Gemfile来管理。 2. **Gemfile.lock**:记录项目的精确gem版本,确保在不同环境中部署时保持一致性。 3. **config/**:配置文件夹,...

    Ruby on Rails Guides v2 - Ruby on Rails 4.2.5

    #### 三、创建一个新的Rails项目 - **步骤**:通过命令行使用`rails new project_name`来初始化一个新的Rails项目。 - **结构**:新项目将包含默认的目录结构,如`app`、`config`、`db`等,分别用于存放应用程序...

    《Ruby On Rails》 源码 下载、导入、运行

    本书源码 博文链接:https://msnvip.iteye.com/blog/139752

    ruby on rails社区网站开发源码

    13. **社区插件(Gems)**:Rails的生态系统中有大量高质量的第三方Gem,如Devise(用户认证)、Pundit(授权管理)、Paperclip或Carrierwave(文件上传)等,它们可以极大地扩展Rails的功能。 通过研究这个源码,...

    Web开发敏捷之道--应用Rails进行敏捷Web开发 之 Depot代码。

    在阅读这个博客后,读者可以学习到敏捷开发的原则,如迭代开发、持续集成以及如何在Rails框架中实现这些原则。 从给出的文件列表中,我们可以分析出Rails项目的典型结构: 1. **Rakefile**:这是Rails项目中的任务...

    Ruby on Rails入门例子

    Ruby on Rails,简称Rails,是一种基于Ruby语言的开源Web应用程序框架,它遵循MVC(Model-View-Controller)架构模式,旨在使Web开发过程更加高效、简洁。本篇将通过一个入门实例,深入探讨Rails的基本概念和核心...

    influxdb-rails-源码.rar

    《InfluxDB与Rails集成深度解析——以influxdb-rails源码为例》 InfluxDB,作为一款专为时间序列数据设计的高性能数据库,被广泛应用于监控、物联网、大数据分析等领域。Rails,作为Ruby on Rails框架的核心部分,...

    Rails 101 入门电子书

    - 安装Rails: 使用gem工具安装最新的Rails版本。 - 测试安装: 创建一个简单的Rails应用来验证是否成功安装。 #### 五、练习作业0-Hello World - **目标**: - 学习如何创建第一个Rails应用程序。 - **过程**: -...

    (Unity源码)街机外星风格射击游戏源码On Rails Shooter Template 1.20.rar

    2-94街机外星风格射击游戏源码On Rails Shooter Template 1.202-94街机外星风格射击游戏源码On Rails Shooter Template 1.202-94街机外星风格射击游戏源码On Rails Shooter Template 1.202-94街机外星风格射击游戏...

    Agile Web Development with Rails 2nd Edition源码

    3. **Model-View-Controller (MVC)**: MVC是Rails的核心设计模式,将应用程序分为三个主要部分:模型负责业务逻辑,视图负责展示数据,控制器处理用户请求并协调模型和视图。源码中可以看到各个部分的组织结构和它们...

Global site tag (gtag.js) - Google Analytics