我多次听到Ruby吹嘘它的超壮观的元编程功能,我想知道是否有人能帮助我开始解决这个问题。
我有一个类,作为一个“归档”类型,内部方法根据输入处理和输出数据。但是,为了性能目的,类本身的存档中的项是用整数表示和处理的。存档之外的实际项是通过字符串表示形式知道的,它只是number_representation.to_s(36)。
正因为如此,我用“代理方法”将每个内部方法连接起来,该方法将输入转换为归档识别的整数形式,运行内部方法,并将输出(单个其他项或它们的集合)转换回字符串。
命名约定如下:内部方法由_method_name表示;它们对应的代理方法由method_name表示,没有前导下划线。
例如:
class Archive
## PROXY METHODS ##
## input: string representation of id's
## output: string representation of id's
def do_something_with id
result = _do_something_with id.to_i(36)
return nil if result == nil
return result.to_s(36)
end
def do_something_with_pair id_1,id_2
result = _do_something_with_pair id_1.to_i(36), id_2.to_i(36)
return nil if result == nil
return result.to_s(36)
end
def do_something_with_these ids
result = _do_something_with_these ids.map { |n| n.to_i(36) }
return nil if result == nil
return result.to_s(36)
end
def get_many_from id
result = _get_many_from id
return nil if result == nil # no sparse arrays returned
return result.map { |n| n.to_s(36) }
end
## INTERNAL METHODS ##
## input: integer representation of id's
## output: integer representation of id's
private
def _do_something_with id
# does something with one integer-represented id,
# returning an id represented as an integer
end
def do_something_with_pair id_1,id_2
# does something with two integer-represented id's,
# returning an id represented as an integer
end
def _do_something_with_these ids
# does something with multiple integer ids,
# returning an id represented as an integer
end
def _get_many_from id
# does something with one integer-represented id,
# returns a collection of id's represented as integers
end
end如果id.class ==字符串位于内部方法的开头,则有几个原因不能将它们转换:
这些内部方法在某种程度上是计算密集型的递归函数,我不希望在每个步骤中多次检查的开销,除非添加额外的参数,否则无法判断是否在最后重新转换,我想把这看作是理解ruby meta-programming的一个练习。
有人有什么想法吗?
编辑
我想要的解决方案最好能够获得一个方法名数组
@@PROXY_METHODS = [:do_something_with, :do_something_with_pair,
:do_something_with_these, :get_many_from]迭代它们,在每次迭代中,输出代理方法。我不知道如何处理这些参数,但是有没有方法来测试一个方法的参数呢?如果没有,那么简单的鸭子类型/类似概念也可以。
我已经想出了自己的解决方案,使用#class_eval
@@PROXY_METHODS.each do |proxy|
class_eval %{ def #{proxy} *args
args.map! do |a|
if a.class == String
a.to_i(36)
else
a.map { |id| id.to_i(36) }
end
end
result = _#{proxy}(*args)
result and if result.respond_to?(:each)
result.map { |r| r.to_s(36) }
else
result.to_s(36)
end
end
}
end然而,#class_eval似乎是bit...messy吗?或者与“应该”相比不雅致。
发布于 2010-06-14 19:51:27
class Archive
# define a new method-creating method for Archive by opening the
# singleton class for Archive
class << Archive
private # (make it private so no one can call Archive.def_api_method)
def def_api_method name, &defn
define_method(name) do |*args|
# map the arguments to their integer equivalents,
# and pass them to the method definition
res = defn[ *args.map { |a| a.to_i(36) } ]
# if we got back a non-nil response,
res and if res.respond_to?(:each)
# map all of the results if many returned
res.map { |r| r.to_s(36) }
else
# map the only result if only one returned
res.to_s(36)
end
end
end
end
def_api_method("do_something_with"){ |id| _do_something_with(id) }
def_api_method("do_something_with_pair"){ |id_1, id_2| _do_something_with_pair id_1.to_i(36), id_2.to_i(36) }
#...
end与打开单例来定义Archive.def_api_method不同,您可以简单地使用
class Archive
def Archive.def_api_method
#...但是我没有这样做的原因是,任何能够访问Archive类的人都可以使用Archive.def_api_method调用它。打开单例类允许我将def_api_method标记为私有,因此只能在self == Archive时调用它。
如果您总是使用相同(或可派生)的名称调用内部版本,那么可以使用#send直接调用它(而不是传递定义块)。
class Archive
# define a method-creating method that wraps an internal method for external use
class << Archive
private # (make it private so no one can call Archive.api_method)
def api_method private_name
public_name = private_name.to_s.sub(/^_/,'').to_sym
define_method(public_name) do |*args|
# map the arguments to their integer equivalents,
# and pass them to the private method
res = self.send(private_name, *args.map { |a| a.to_i(36) })
# if we got back a non-nil response,
res and if res.respond_to?(:each)
# map all of the results if many returned
res.map { |r| r.to_s(36) }
else
# map the only result if only one returned
res.to_s(36)
end end
# make sure the public method is publicly available
public public_name
end
end
api_method :_do_something_with
api_method :_do_something_with_pair
private
def _do_something_with
#...
end
def _do_something_with_pair
#...
end
end这更像是其他元方法(如attr_reader和attr_writer )所做的事情。
https://stackoverflow.com/questions/3040263
复制相似问题