0

私は次のクラスを定義しています。これらのクラスには、マイナーなバリエーションを含む多くの共通コードがあります。

class ThirdPartyComponent < ActiveRecord::Base
  belongs_to :prev_version, :class_name => 'ThirdPartyComponent', :foreign_key => 'prev_version_id'
  has_one :next_version, :class_name => 'ThirdPartyComponent', :foreign_key => 'prev_version_id'

  attr_accessible :name, :version, :installer, :install_script

  mount_uploader :installer, ComponentFileUploader
  mount_uploader :install_script, ComponentFileUploader

  validates :name, :presence => true
  validates :version, :presence => true, :format => { :with => /\A\d{1,3}\.\d{1,2}\z/ }
  validates :installer, :presence => true
  validates :install_script, :presence => true
  validate :increased_version

  def increased_version
    # Check to ensure that version number is greater than the previous version number for the same component set
    unless prev_version.nil?
      version > prev_version.version
    end
  end 

  def all_previous_versions
    prev_versions = all_versions
    prev_versions.shift
    prev_versions
  end

  def all_versions
    current_version = self
    all_versions = [current_version]
    while !current_version.prev_version.nil?
      all_versions << current_version.prev_version
      current_version = current_version.prev_version
    end
    all_versions
  end
end

class RegistryComponent < ActiveRecord::Base

  belongs_to :prev_version, :class_name => 'RegistryComponent', :foreign_key => 'prev_version_id'
  has_one :next_version, :class_name => 'RegistryComponent', :foreign_key => 'prev_version_id'

  attr_accessible :name, :version, :registry_file

  mount_uploader :registry_file, ComponentFileUploader

  validates :name, :presence => true
  validates :version, :presence => true, :format => { :with => /\A\d{1,3}\.\d{1,2}\z/ }
  validates :registry_file, :presence => true
  validate :increased_version

  def increased_version
    # Check to ensure that version number is greater than the previous version number for the same component set
    unless prev_version.nil?
      version > prev_version.version
    end
  end

  def all_previous_versions
    prev_versions = all_versions
    prev_versions.shift
    prev_versions
  end

  def all_versions
    current_version = self
    all_versions = [current_version]
    while !current_version.prev_version.nil?
      all_versions << current_version.prev_version
      current_version = current_version.prev_version
    end
    all_versions
  end
end

また、非常によく似た機能を持つ他のコンポーネントを将来的に追加することも検討しています。

これらのクラスから共通のコードを単一のファイル (validate などの ActiveRecord メソッド呼び出しを含む) に抽出し、具体的なクラスでそれらを参照するだけです。

これまで試してきたのは、

  1. 継承 - ActiveRecord から継承した基本クラスを作成し、次に各クラスを基本クラスから継承しました。結果: Rails は、名前が基本クラスと一致するデータベース テーブルが見つからないと不平を言いました。
  2. 継承 - 代わりにテーブルレス モデルとして基本クラスを作成することを検討しましたが ( http://railscasts.com/episodes/219-active-modelを参照)、具体的なクラスにも完全な ActiveRecord 機能がないことに気付きました
  3. 構成 - モジュールで共通コードを定義してから、具体的なクラスでインクルードまたはエクステンドを使用して、以下に示すようにアクセスしようとしました。

    module ComponentBase
      belongs_to :prev_version, :class_name => self.class.name, :foreign_key => 'prev_version_id'
      has_one :next_version, :class_name => self.class.name, :foreign_key => 'prev_version_id'
    
      attr_accessible :name, :version
    
      validates :name, :presence => true
      validates :version, :presence => true, :format => { :with => /\A\d{1,3}\.\d{1,2}\z/ }
      validate :increased_version
    
      def increased_version
        # Check to ensure that version number is greater than the previous version number for the same component set
        unless prev_version.nil?
          version > prev_version.version
        end
      end
    
      def all_previous_versions
        prev_versions = all_versions
        prev_versions.shift
        prev_versions
      end
    
      def all_versions
        current_version = self
        all_versions = [current_version]
        while !current_version.prev_version.nil?
          all_versions << current_version.prev_version
          current_version = current_version.prev_version
        end
        all_versions
      end
    end
    
    class RegistryComponent < ActiveRecord::Base
      include ComponentBase
    
      attr_accessible :registry_file
    
      mount_uploader :registry_file, ComponentFileUploader
    
      validates :registry_file, :presence => true
    end
    

    belongs_toに対してメソッドが定義されていないというエラーが発生しましたComponentBase。これは最も有望な解決策に見えますが、それらを含むクラスのコンテキスト内で ActiveRecord クラス メソッドを実行する方法はありますか? あるいは、同じ目的を達成するためのより良い方法はありますか?

4

3 に答える 3

1

あなたの最初の選択肢は、実際には最良の選択肢でした。Rails は単一テーブル継承を使用します。つまり、すべてのサブクラスのデータが同じテーブルに保持されるため、エラーが発生しました。

と呼ばれる新しいモデルを作成し、Componentすべてのコンポーネントに共通するすべてのフィールドとtype、文字列フィールドである必要がある 1 つの追加フィールドをそれに追加する必要があります。

コンポーネント モデルには、すべての共通フィールド、ロジック、および検証が含まれます。

class Component < ActiveRecord::Base
  ...
end

次に、各コンポーネント クラスをコンポーネント サブクラスにします。

class ThirdPartyComponent < Component
  ...
end
于 2013-07-10T03:17:52.910 に答える
0

問題は、モジュール自体ではなく、モジュールを含むクラスでを実行するには、belongs_to メソッドが必要なことです。

module#included http://www.ruby-doc.org/core-2.0/Module.html#method-i-includedを確認してください。これにより、モジュールを含めるモジュールでコードを実行できます。Module は Class の祖先であるため、これはクラスとモジュールで機能することに注意してください。

この場合、モジュールがインクルードされるクラスで属し_to を実行する必要があるため、次のようなものを使用して作業できる例として機能する必要があります。

module ComponentBase
    def self.included(mod)
        mod.class_eval do
            belongs_to :prev_version, :class_name => self.class.name, :foreign_key => 'prev_version_id'
        end
    end
end
于 2013-07-10T01:06:20.557 に答える
0

スタンドアロンのコードを使用して Ruby クラスを拡張するで次の回答に出くわし、少し実験して機能させました。最終的なコードは、

module ComponentBase

  def self.included(base)
    base.class_eval do
      belongs_to :prev_version, :class_name => base, :foreign_key => 'prev_version_id'
      has_one :next_version, :class_name => base, :foreign_key => 'prev_version_id'

      attr_accessible :name, :version

      validates :name, :presence => true
      validates :version, :presence => true, :format => { :with => /\A\d{1,3}\.\d{1,2}\z/ }
      validate :increased_version
    end
  end

  def increased_version
    # Check to ensure that version number is greater than the previous version number for the same component set
    unless prev_version.nil?
      version > prev_version.version
    end
  end

  def all_previous_versions
    prev_versions = all_versions
    prev_versions.shift
    prev_versions
  end

  def all_versions
    current_version = self
    all_versions = [current_version]
    while !current_version.prev_version.nil?
      all_versions << current_version.prev_version
      current_version = current_version.prev_version
    end
    all_versions
  end
end

class RegistryComponent < ActiveRecord::Base
  include ComponentBase

  attr_accessible :registry_file

  mount_uploader :registry_file, ComponentFileUploader

  validates :registry_file, :presence => true
end

included解決策は、モジュールが別の場所に含まれるたびに呼び出されるコールバックを使用することでした。次に、基本モジュールを呼び出しclass_evalて、メソッドをクラス コンテキストで (つまり、クラス メソッドとして) 実行します。最も難しい部分は、このコンテキストでクラス名を取得することでしたが、使用できることが判明しましたbase(理由は完全にはわかりませんが、機能します)。

于 2013-07-10T01:08:44.647 に答える