Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyのコードローダー:Zeitwerkを理解する

Rubyのコードローダー-Zeitwerkを理解する

Zeitwerkを使用すると、クラスとモジュールがどこでも利用できることを知って、プログラミングを合理化できます。

コードローダーとは何ですか?

コードローダーを使用すると、開発者はclassesを定義できます。 およびmodules さまざまなファイルやフォルダにまたがって、明示的に要求することなくコードベース全体でそれらを使用します。 Railsは、コードローダーを使用するソフトウェアの良い例です。 Railsでのプログラミングには、明示的なrequireは必要ありません。 コントローラで使用する前にモデルをロードするための呼び出し。実際、Rails 6では、appのすべてが ディレクトリは、いくつかの例外を除いて、アプリの起動時に自動ロードされます。

コードの読み込みはrequireを呼び出すことがすべてだと考えるのは簡単ですが 、それはそれほど単純ではありません。コードの読み込みは、次のようにさらに3つの部分に分けることができます。

  • 自動読み込み: これは、コードが必要に応じてオンザフライでロードされることを意味します。たとえば、Railsでは、rails sを実行します すべてのモデル、コントローラーなどをロードするわけではありません。ただし、モデルの最初のヒット時にUser 、自動読み込みメカニズムを実行して、モデルを見つけて使用します。これは実際の自動読み込みです。より高速なアプリとrails consoleがあるため、これには開発環境にいくつかの利点があります。 起動時間。 Rails.config.autoload_path 自動ロードされるパスを制御します。
  • 積極的な読み込み: これは、アプリの起動時にコードがメモリに読み込まれ、定数が呼び出されるのを待たずにコードを要求することを意味します。 Railsでは、コードは本番環境で熱心にロードされます。上記の説明から、本番環境でコードを自動ロードすると、各定数がオンザフライで必要になるため、応答時間が遅くなります。 Rails.config.eager_load_paths 熱心にロードされるパスを制御します。
  • リロード: コードローダーは、autoload_path内のファイルへの変更を常に監視しています。 変更に気付いたときにファイルをリロードします。 Railsでは、これによりrails sを実行できるため、開発に非常に役立ちます。 同時に、Railsサーバーを再起動せずに変更を加えます。これは実際にリロードしています。

これらの概念のほとんどがRailsで開発され、実行されていることが簡単にわかります。 Zeitwerkはこれを変更します! Zeitwerkを使用すると、すべてのコード読み込みアクションを任意のRubyプロジェクトに導入できます。

Zeitwerkとは何ですか?

ZeitwerkはRuby用の効率的でスレッドセーフなコードローダーであり、Webフレームワーク(Rails、Hanami、Sinatra)、Cliツール、gemなどのRubyプロジェクトで使用できます。これを使用すると、クラスとモジュールがどこでも利用できることを知って、プログラミングを合理化できます。伝統的に、Railsと一部 他のgemには、この機能を有効にするためのコードローダーが組み込まれています。ただし、Zeitwerkはこれらの概念を宝石に抽出し、Rubyistがこれらの概念をプロジェクトに適用できるようにします。

Zeitwerkのインストール

まず最初に、gemをインストールする必要があります:

gem install zeitwerk

# OR in your Gemfile
gem 'zeitwerk', '~> 2.4.0'

Zeitwerkの構成

それでは、基本から始めましょう:

require 'zeitwerk'
loader = Zeitwerk::Loader.new
...
loader.setup

上記のコードはローダーインスタンスをインスタンス化し、setupを呼び出します 。 setupを呼び出した後 、ローダーはコードをロードする準備ができています。ただし、その前に、loaderで必要なすべての構成 すでにカバーされている必要があります。この記事では、loaderのいくつかの構成について説明します。 およびコードを構造化するための規則。

  • ファイル構造:Zeitwerkが機能するには、ファイルとディレクトリ名が、それらが定義するモジュールとクラス名と一致している必要があります。たとえば、
  lib/my_gem.rb         -> MyGem
  lib/my_gem/foo.rb     -> MyGem::Foo
  lib/my_gem/bar_baz.rb -> MyGem::BarBaz
  lib/my_gem/woo/zoo.rb -> MyGem::Woo::Zoo
  • ルート名前空間:ルート名前空間は、Zeitwerkが存在するディレクトリです。 あなたのコードを見つけることができます。 modulesの場合 およびclasses が参照されている場合、Zeitwerkは一致するファイル名でルート名前空間を検索することを認識しています。たとえば、
  require 'zeitwerk'
  loader = Zeitwerk::Loader.new
  loader.push_dir("app/models")
  loader.push_dir("app/controllers")

  // matches as follows
  app/models/user.rb                        -> User
  app/controllers/admin/users_controller.rb -> Admin::UsersController

2つの異なるユースケースのルート名前空間を定義する主な方法は2つあります。デフォルトの方法を以下に示します:

  // init.rb
  require 'zeitwerk'
  loader = Zeitwerk::Loader.new
  loader.push_dir("#{__dir__}/bar")
  ...
  loader.setup

  // bar/foo.rb
  class Foo; end

これは、クラスFooを意味します 明示的なBar::Fooなしで参照できます 、barディレクトリがルート名前空間として機能するため。名前空間を定義する2番目の方法は、push_dirの呼び出しで名前空間を明示的に指定することです。 :

  // init.rb
  require 'zeitwerk'

  module Bar
  end
  loader = Zeitwerk::Loader.new
  loader.push_dir("#{__dir__}/src", namespace: Bar)
  loader.setup

  // src/foo.rb
  class Bar::Foo; end

このコードには注意すべき点がいくつかあります:

  1. モジュールBar push_dirによって使用される前にすでに定義されています 。使用するモジュールがサードパーティによって定義されている場合、push_dirの呼び出しで使用する前に、単純なrequireによってモジュールが定義されます。 。
  2. push_dir 名前空間Barを明示的に指定します 。
  3. ファイルsrc/foo.rb 定義されたBar::FooFooではありません 、src/bar/foo.rbのようにディレクトリを作成する必要はありませんでした 。
  • 独立したコードローダー:設計上、Zeitwerkでは、各プロジェクトまたはアプリの依存関係で個々のプロジェクトツリーを管理できます。これは、各依存関係のコード読み込みメカニズムがその依存関係によって管理されることを意味します。たとえば、Rails 6では、ZeitwerkはRailsアプリのコードの読み込みを処理し、各gem依存関係が独自のプロジェクトツリーを個別に管理できるようにします。複数のコードローダー間でファイルが重複しているのはエラー状態です。

  • 自動読み込み:上記の設定で、setupを呼び出したら 作成されると、すべてのクラスとモジュールがオンデマンドで利用できるようになります。

  • 再読み込み:再読み込みを有効にするには、loader 明示的に構成する必要があります。たとえば、

  loader = Zeitwerk::Loader.new
  ...
  loader.enable_reloading # you need to opt-in before setup
  loader.setup
  ...
  loader.reload

loader.reload callはプロジェクトツリーをオンザフライでリロードし、新しい変更はすぐに表示されます。ただし、ファイルシステムへの変更を検出し、loader.reloadを呼び出すには、周囲のメカニズムが必要です。 。簡単なバージョンを以下に示します:

  require 'filewatcher'

  loader = Zeitwerk::Loader.new
  ...
  loader.enable_reloading
  loader.setup
  ...

  my_filewatcher = Filewatcher.new('lib/')
  Thread.new(my_filewatcher) {|fw| fw.watch {|filename| loader.reload } }

RailsでのZeitwerkの使用

ZeitwerkはRails6.0ではデフォルトで有効になっています。ただし、オプトアウトしてRails classicを使用することはできます コードローダー。

# config/application.rb
config.load_defaults "6.0"
config.autoloader = :classic
宝石でのZeitwerkの使用

Zeitwerkは、標準のgem構造(lib/special_gem)を使用している限り、gemに便利なメソッドを提供します。 )。この便利な方法は、次のように使用できます。

# lib/special_gem.rb
require 'zeitwerk'

module SpecialGem
end

loader = Zeitwerk::Loader.for_gem
loader.setup

標準のgem構造では、for_gem 呼び出しはlibを追加します ルート名前空間としてのディレクトリ。lib内のすべてのコードを有効にします 自動的に見つかるディレクトリ。

さらにインスピレーションを得るために、Zeitwerkを使用して宝石をチェックすることができます:

  • カラフカ
  • ジェット
参照

Railsの自動読み込み—どのように機能し、いつ機能しないか

Zeitwerk


  1. Rubyでの複製とクローン:違いを理解する

    Rubyでオブジェクトをコピーできることをご存知ですか?それだけでなく、これを行うには2つの異なる方法があります! これらの方法は : dup clone すぐに違いを探りますが、最初に… なぜオブジェクトのクローンを作成するのですか? ? Rubyの多くのオブジェクトは変更可能であり、変更することができます。 オブジェクトを変更したいが、元のコピーを保持する場合 その後、クローンを作成できます。 たとえば。 最初の要素を除くすべての要素を含む配列が必要な場合があります。 これを行う1つの方法 : a = [1,2,3,4,5] a[1..-1] # [2,3,4

  2. Rubyでの静的分析

    ソースコードを解析して、すべてのメソッド、それらが定義されている場所、およびそれらが取る引数を見つけたいとします。 どうすればこれができますか? あなたの最初のアイデアはそれのために正規表現を書くことかもしれません… しかし、もっと良い方法はありますか? はい! 静的分析 は、ソースコード自体から情報を抽出する必要がある場合に使用できる手法です。 これは、ソースコードをトークンに変換する(解析する)ことによって行われます。 さっそく始めましょう! パーサージェムの使用 Rubyには標準ライブラリで利用可能なパーサーがあります。名前はRipperです。出力を操作するのは難し