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

Rubyモジュールをネストするときは、これらのトラップを避けてください

モジュール(およびクラス)はネストすることを目的としています。 ActiveRecord::RecordNotFoundのようなコードフラグメント 非常に一般的であるため、それらについて二度と考えることはありません。しかし、Rubyのネスト実装(およびRailsの自動ロードシステム)には、コードが奇妙で素晴らしい方法で失敗する原因となる可能性のあるいくつかのトラップが埋め込まれています。この投稿では、これらのトラップの原因と、それらを回避する方法について説明します。

定数とは何ですか?

この投稿はモジュールに関するものですが、モジュールを理解するには定数を理解する必要があります。ほとんどの言語では、以下の例のように、定数はデータのほんの一部を格納するためにのみ使用されます。

# These are simple constants
MAX_RETRIES = 5
DEFAULT_LANGUAGE = "en"

しかし、Rubyでは、クラスとモジュールも定数です。これを示すために、小さな例を書きました。以下のモジュールには、数値、クラス、ネストされたモジュールの3つの定数があります。ネストされたクラスまたはモジュールにアクセスすると、rubyは、単純な数値定数で使用するのと同じルールを使用してそれを見つけます。

module MyModule
  MY_FAVORITE_NUMBER = 7

  # Classes are constants
  class MyClass
  end

  # So are modules
  module MyEmbeddedModule
  end
end

puts MyModule.constants.inspect # => [:MY_FAVORITE_NUMBER, :MyClass, :MyEmbeddedModule]

多くの場合、モジュールは親で定義された定数にアクセスできます。そのため、次のようなコードを記述できます。

module X
  MARCO = "polo"
  module Y
    def self.n 
      puts MARCO
    end
  end
end

X::Y.n() # => "polo"

しかし、この親子関係がYがXの定数MARCOにアクセスできるようにするものであると考えた場合、あなたは間違っているでしょう。

一般的な問題

上記のコードを少し異なる方法で書き直すと、驚くべきことが起こります。 YはX::MARCOにアクセスできなくなります。ここで一体何が起こっているのですか?

module A
  MARCO = "polo"
end

module A::B
  def self.n 
    puts MARCO  # => uninitialized constant A::B::MARCO (NameError)
  end
end

A::B.n()

子による親の定数の「継承」は、親と子の関係によるものではないことがわかります。語彙です。つまり、コードが構築しているオブジェクトの構造ではなく、コードの構造に基づいているということです。

ネストの検査

Rubyがネストされた定数を検索する方法をより深く理解したい場合は、Module.nestingを確認する価値があります。 機能。

この関数は、指定されたスコープ内の定数の「検索パス」を構成するオブジェクトの配列を返します。前の例のネストを調べてみましょう。

最初の例では、ネストが[A::B, A]であることがわかります。 。つまり、定数MARCOを使用する場合、Rubyは最初にA::Bでそれを検索します。 、次にA

module A
  MARCO = "polo"
  module B
    def self.n 
      puts Module.nesting.inspect  # => [A::B, A]
      puts MARCO # => "polo"
    end
  end
end

2番目の例では、ネストにA::Bのみが含まれていることがわかります。 、Aではありません 。 BはAの「子」ですが 、私がコードを書いた方法では、ネストされたものとして表示されないため、この目的ではネストされていない可能性があります。

module A
  MARCO = "polo"
end

module A::B
  def self.n 
    puts Module.nesting.inspect  # => [A::B]
    puts MARCO # => uninitialized constant A::B::MARCO (NameError)
  end
end
Railsの自動ロードの複雑化

Railsを使用するときにファイルを含める必要がないことに気付いたことがありますか?モデルを使用する場合は、それを使用するだけです。

これが可能なのは、Railsが自動ロードシステムを実装しているためです。 Module.const_missingを使用します ロードされていない定数を参照しようとしたときに検出します。次に、定数が含まれていると思われるファイルをロードします。これはほとんどの場合機能しますが、問題があります。

Railsは、モジュールが常に可能な限り最大のネストを持っていることを前提としています。モジュールA::B::Cには[A::B::C, A::B, A]のネストがあることを前提としています。 。そうしないと、予期しない動作が発生します。

以下のコードでは、モジュールBがA::MARCOにアクセスできないようにする必要があります。 。通常のRubyでは、ネストが[A ::B]であるため、これはできません。したがって、例外が発生するはずです。しかし、Railsの自動ロードは例外をスローしません。代わりに、A::MARCOを返します 。

# a.rb
module A
  MARCO = "polo"
end

# a/b.rb
module A::B
  def self.n 
    puts MARCO # => "polo"
  end
end

# some_controller.rb
A::B.n()

結論?

これはすべて、考えることがたくさんあります。可能な限り考えないようにしたいので、モジュールA::Bには近づかないようにしています。 構文。モジュールの入れ子を意図的に操作したい場合は考えられません。ご存知の方はぜひお聞かせください!


  1. Google でこれら 12 のことを検索するときは注意してください

    間違いなく、私たち全員が求める情報を入手するためのワンストップ プラットフォームである Google は、ありとあらゆるものに対する私たちの答えとなっています。近くの銀行の支店のような小さな情報を見つけることから、市場の巨大な巨人によって提供される新しい更新のような大きな情報を取得することまで、私たちは Google にアクセスします。私たちがどんな種類の質問をしていても、それがどれほどばかげているように聞こえても、常に私たちが望む結果、または少なくとも答えに到達するための最も関連性の高いパスを提供してくれます。 Google は私たちが実行する日常業務の一部になりました。クエリがあると

  2. これらの Windows メンテナンスの間違いを避ける

    テクノロジの進歩により、ユーザーは Windows コンピュータをメンテナンスし、定期的にクリーニングして正常な状態に保つ基本的な方法を理解しています。ただし、時間の経過とともに、OS、特に Windows 10 が大幅に変更されたため、メンテナンス方法も変更されました。しかし、ユーザーはそれを知らされていないため、依然として古い方法を使用してコンピューターの正常性を維持しているため、システムの正常性に悪影響を及ぼす可能性があります. 心配しないでください。この記事では、コンピューターの状態を維持しながら避けるべきよくある間違いについて説明しました。 1. Windows Update