2016-12-08 67 views
0

我的工作增加了一個功能,Rails的模型,目前有以下結構的寶石模型實例方法:如何安全地包括寶石

# lib/my_gem/loader.rb 

module MyGem 
    module Loader 
    def loader 
     include I18nAttributes::InstanceMethods 
    end 
    end 
end 
ActiveRecord::Base.send :extend, MyGem::Loader 
# lib/my_gem/instance_methods.rb 

module MyGem 
    module InstanceMethods 
    def foo 
     puts '-> foo from gem' 
     bar      # <-- HERE 
    end 

    def bar 
     puts '-> bar from gem' 
    end 
    end 
end 

在Rails模型,該功能現在加入:

# app/models/model.rb 

class Model < ActiveRecord::Base 
    loader 
end 

這使得從寶石的實例方法的模型實例訪問:

irb(main):001:0> m = Model.new 
irb(main):002:0> m.foo 
-> foo from gem 
-> bar from gem 

然而,當模型實現了它自己的bar

# app/models/model.rb 

class Model < ActiveRecord::Base 
    loader 

    def bar 
    puts '-> bar from model' 
    end 
end 

從模型中bar方法陰影:

irb(main):001:0> m = Model.new 
irb(main):002:0> m.foo 
-> foo from gem 
-> bar from model 

以上都不是一個驚喜,但風險應避免碰撞方法:

  • 上面的寶石代碼中是否有bar -line的替代方案(標記爲HERE),它確保在模塊中定義的bar-方法被使用,即使該模型定義了它自己的bar版本?

  • 有沒有更好的模式可以消除這種風險?

感謝您的提示!


UPDATE

不污染來自寶石內部方法的模型實例將是一個嵌套類的一種方式:

# lib/my_gem/instance_methods.rb 

module MyGem 
    module InstanceMethods 
    class Inernal 
     def bar 
     puts '-> bar from gem' 
     end 
    end 

    def foo 
     puts '-> foo from gem' 
     Internal.new.bar 
    end 
    end 
end 

將是因爲我的情況很方便嵌套類可以擁有自己的一組實例變量。你怎麼看,一個可以接受的模式?

回答

0

該模型能夠覆蓋你的方法,並且你無能爲力。這對Ruby的繼承如何工作至關重要。我不確定爲什麼你會盡自己的方法來防止這種事情發生,即使這是可能的。

大多數寶石採取的方法是保持儘可能最小的模塊的佔地面積。這通常意味着移動的方法,你通常有私有實例方法到另一個模塊,使他們最終不會污染目標類:

module MyExtension 
    module Stub 
    def loaded 
     include MyExtension::InstanceMethods 
    end 
    end 

    module InstanceMethods 
    def bar 
     MyExtension::Support.format(:bar) 
    end 
    end 

    module Support 
    def self.format(text) 
     "#{text} from module" 
    end 
    end 
end 

這意味着只有在Stub定義的方法是在類級別進口。其餘的按需加載。

擴展你的基類:

class ModelBase 
end 

ModelBase.send(:extend, MyExtension::Stub) 

在派生類中,然後使用它:

class MyModel < ModelBase 
    loaded 
end 

MyModel.new.bar 

還是有沒有辦法避免覆蓋。