我正在开发一个gem,它为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
这使模型实例可以访问gem中的实例方法:
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
以上都不是一个惊喜,但应避免碰撞方法的风险:
上面的gem代码(标有bar
)中是否有HERE
- 行的替代方法,以确保模块中定义的bar
- 方法甚至可以使用如果模型定义了它自己的bar
版本?
是否有更好的模式可以消除这种风险?
感谢您的提示!
更新
使用gem中的内部方法不污染模型实例的方法是嵌套类:
# 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 :(得分: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
仍然无法避免覆盖。