config/initializers

昨日銀座Railsへ行きました。

そこでgemつくる前にconfig/initializers配下に.rbファイル作って試せるということを知りました!

さらにrailsを速く動かすために何を変えればいいかコードを追っているのをみて、自分も少しコードを見たくなったのでconfig/initializersについて調べつつ定義元を読んでみることにしました!

 

 

config/initializersとは

アプリを起動した際まとめてロードされるディレクトリです。

具体的には、フレームワークの読み込み&gemの読み込みが終わったタイミングで読み込まれます。

開発環境・テスト環境・本番環境全てで読み込まれます。

gemを入れるとそのgemの設定ファイルが追加されたりします。自分で.rbファイルを作成して追加することも可能です。

デフォルトで以下の.rbファイルが入ってます。

  • application_controller_renderer.rb
  • assets.rb
  • backtrace_silencers.rb
  • content_security_policy.rb
  • cookies_serializer.rb
  • filter_parameter_logging.rb
  • inflections.rb
  • mime_types.rb
  • wrap_parameters.rb

 

 

 

どうやって読み込まれてるか

initializers配下の.rbファイルを読み込む設定はrails/rails側の、

rails/railties/lib/rails/initializable.rbで書かれています。

# frozen_string_literal: true

require "tsort"

module Rails
module Initializable
def self.included(base) #:nodoc:
base.extend ClassMethods
end

class Initializer
attr_reader :name, :block

def initialize(name, context, options, &block)
options[:group] ||= :default
@name, @context, @options, @block = name, context, options, block
end

def before
@options[:before]
end

def after
@options[:after]
end

def belongs_to?(group)
@options[:group] == group || @options[:group] == :all
end

def run(*args)
@context.instance_exec(*args, &block)
end

def bind(context)
return self if @context
Initializer.new(@name, context, @options, &block)
end

def context_class
@context.class
end
end

class Collection < Array
include TSort

alias :tsort_each_node :each
def tsort_each_child(initializer, &block)
select { |i| i.before == initializer.name || i.name == initializer.after }.each(&block)
end

def +(other)
Collection.new(to_a + other.to_a)
end
end

def run_initializers(group = :default, *args)
return if instance_variable_defined?(:@ran)
initializers.tsort_each do |initializer|
initializer.run(*args) if initializer.belongs_to?(group)
end
@ran = true
end

def initializers
@initializers ||= self.class.initializers_for(self)
end

module ClassMethods
def initializers
@initializers ||= Collection.new
end

def initializers_chain
initializers = Collection.new
ancestors.reverse_each do |klass|
next unless klass.respond_to?(:initializers)
initializers = initializers + klass.initializers
end
initializers
end

def initializers_for(binding)
Collection.new(initializers_chain.map { |i| i.bind(binding) })
end

def initializer(name, opts = {}, &blk)
raise ArgumentError, "A block must be passed when defining an initializer" unless blk
opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] }
initializers << Initializer.new(name, nil, opts, &blk)
end
end
end
end

 

def self.included(base) #:nodoc:
    base.extend ClassMethods
end

この部分でRails::Initializableモジュールを生やしたクラスのクラスメソッドとして、ClassMethodsクラスに定義しているinitializers、initializers_chain、initializers_for、initializerメソッドを使えるようにしているのかな??

 

 

実際にそれらのメソッドが呼ばれるのは

rails/railties/lib/rails/application.rbのinitialize!メソッドの中のようです。

def initialize!(group = :default) #:nodoc:
raise "Application has been already initialized." if @initialized
run_initializers(group, self)
@initialized = true
self
end

 

 

さらにinitialize!メソッドが呼ばれるのはRailsアプリ側のconfig/environment.rbの中のようです。

# Load the Rails application.
require_relative 'application'

# Initialize the Rails application.
Rails.application.initialize!

 

 

 

感想その他

まだまだ追いきれておりませんが、今日は一旦ここまでにします。。。!!

明日以降続きや、initializersが呼ばれるより前の部分も見ていきたいと思います!!