2013-07-10 23 views
0

我有以下定義的類,它們有許多具有較小變化的通用代碼。爲類似的句柄提取常見的ActiveRecord代碼

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 

我也在尋找在未來添加一些其他組件,再次具有非常相似的功能。

我想從這些類中提取通用代碼到單個文件(包括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類方法嗎?或者,有沒有更好的方式來達到同樣的目標?

回答

0

問題是,您需要belongs_to方法在包含模塊的類上運行,而不是模塊本身。

檢出模塊#包含http://www.ruby-doc.org/core-2.0/Module.html#method-i-included,它允許您在包含模塊的模塊上運行代碼。 Remmeber Module是Class的祖先,因此它適用於類和模塊。

在這種情況下,你會希望在類模塊已被納入運行belongs_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 
+0

請參閱我對以下答案的評論 – richard

0

Extending a Ruby class with a standalone piece of code下面的答案和一些實驗讓它起作用。所以我結束了最後的代碼是,

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(不完全確定爲什麼這是事實,但它的工作原理)。

+0

base是包含模塊的類。 – Puhlze

+0

@Puhlze在刪除它之前,我看到了您的答案,我認爲它比我自己的理解稍微容易理解,但是您必須在兩種解決方案中將'self.class.name'更改爲'base'。 – richard

+0

我將它添加回來並更正它,以包含class_eval方法,以便清楚評論中討論的內容。答案中的'base'是傳遞給方法的參數。 – Puhlze

1

你的第一個選擇其實是最好的選擇。 Rails使用Single Table Inheritance,這意味着所有子類的數據都保存在同一個表中,這就是爲什麼你得到了錯誤的原因。

您應該做的是創建一個名爲Component的新模型,並在其中添加所有組件中通用的所有字段以及一個名爲type的字段,該字段應該是字符串字段。

您的組件模型將包含所有常用字段,邏輯和驗證。

class Component < ActiveRecord::Base 
    ... 
end 

然後讓有每個compontent類的子類組件。

class ThirdPartyComponent < Component 
    ... 
end