我已经多次听到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 == String,我就不能只转换它们有几个原因:
有没有人有任何想法?
修改的
我想要的解决方案最好能够采用一系列方法名称
@@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
似乎有点......凌乱?与“应该”相比,还是不优雅的。
答案 0 :(得分:2)
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
。