我希望能够在模块中拥有包含该模块的类无法访问的方法。给出以下示例:
class Foo
include Bar
def do_stuff
common_method_name
end
end
module Bar
def do_stuff
common_method_name
end
private
def common_method_name
#blah blah
end
end
我希望Foo.new.do_stuff爆炸,因为它试图访问模块试图隐藏它的方法。但是,在上面的代码中,Foo.new.do_stuff可以正常工作:(
有没有办法在Ruby中实现我想做的事情?
更新 - 真实代码
class Place < ActiveRecord::Base
include RecursiveTreeQueries
belongs_to :parent, {:class_name => "Place"}
has_many :children, {:class_name => 'Place', :foreign_key => "parent_id"}
end
module RecursiveTreeQueries
def self_and_descendants
model_table = self.class.arel_table
temp_table = Arel::Table.new :temp
r = Arel::SelectManager.new(self.class.arel_engine).from(model_table).project(model_table.columns).join(temp_table).on('true').where(model_table[:parent_id].eq(temp_table[:id]))
nr = Place.scoped.where(:id => id)
q = Arel::SelectManager.new(self.class.arel_engine)
as = Arel::Nodes::As.new temp_table, nr.union(r)
arel = Arel::SelectManager.new(self.class.arel_engine).with(:recursive,as).from(temp_table).project(temp_table[:id])
self.class.where(model_table[:id].in(arel))
end
def self_and_ascendants
model_table = self.class.arel_table
temp_table = Arel::Table.new :temp
r = Arel::SelectManager.new(self.class.arel_engine).from(model_table).project(model_table.columns).join(temp_table).on('true').where(temp_table[:parent_id].eq(model_table[:id]))
nr = Place.scoped.where(:id => id)
q = Arel::SelectManager.new(self.class.arel_engine)
as = Arel::Nodes::As.new temp_table, nr.union(r)
arel = Arel::SelectManager.new(self.class.arel_engine).with(:recursive,as).from(temp_table).project(temp_table[:id])
self.class.where(model_table[:id].in(arel))
end
end
显然,这段代码被黑了,并且由于一些严重的重构,我的问题的目的是找出是否有一种方法可以重构这个模块而不会因为意外覆盖ActiveRecord :: Base或其他任何方法而忽略某些方法模块包含在Place.rb。
中答案 0 :(得分:5)
我不相信有任何直接的方法可以做到这一点,这是设计的。如果需要封装行为,则可能需要类,而不是模块。
在Ruby中,私有方法和公共方法之间的主要区别是私有方法只能在没有显式接收器的情况下调用。调用MyObject.new.my_private_method
会导致错误,但在my_private_method
中的方法定义中调用MyObject
可以正常工作。
将模块混合到一个类中时,该模块的方法被“复制”到类中:
[我]我们在类定义中包含一个模块,它的方法被有效地附加到或者“混入”到类中。 - Ruby User's Guide
就班级而言,该模块不再作为外部实体存在(但请参阅Marc Talbot在下面的评论)。您可以在类中调用任何模块的方法而无需指定接收器,因此它们实际上不再是模块的“私有”方法,只是该类的私有方法。
答案 1 :(得分:1)
这是一个相当古老的问题,但我觉得有必要回答它,因为接受的答案缺少Ruby的一个关键特性。
该功能称为Module Builders,以下是如何定义模块以实现它:
class RecursiveTreeQueries < Module
def included(model_class)
model_table = model_class.arel_table
temp_table = Arel::Table.new :temp
nr = Place.scoped.where(:id => id)
q = Arel::SelectManager.new(model_class.arel_engine)
arel_engine = model_class.arel_engine
define_method :self_and_descendants do
r = Arel::SelectManager.new(arel_engine).from(model_table).project(model_table.columns).join(temp_table).on('true').where(model_table[:parent_id].eq(temp_table[:id]))
as = Arel::Nodes::As.new temp_table, nr.union(r)
arel = Arel::SelectManager.new(arel_engine).with(:recursive,as).from(temp_table).project(temp_table[:id])
self.class.where(model_table[:id].in(arel))
end
define_method :self_and_ascendants do
r = Arel::SelectManager.new(arel_engine).from(model_table).project(model_table.columns).join(temp_table).on('true').where(temp_table[:parent_id].eq(model_table[:id]))
as = Arel::Nodes::As.new temp_table, nr.union(r)
arel = Arel::SelectManager.new(arel_engine).with(:recursive,as).from(temp_table).project(temp_table[:id])
self.class.where(model_table[:id].in(arel))
end
end
end
现在您可以将模块包含在:
中class Foo
include RecursiveTreeQueries.new
end
你需要在这里实际实例化模块,因为RecursiveTreeQueries
不是模块本身而是类(Module
类的子类)。您可以进一步重构以减少方法之间的大量重复,我只是采取了您必须展示的概念。
答案 2 :(得分:0)
在包含模块时将方法标记为私有。
module Bar
def do_stuff
common_method_name
end
def common_method_name
#blah blah
end
def self.included(klass)
klass.send(:private, :common_method_name)
end
end