我希望创建一个与ActiveRecord::Base
的{{1}}工作方式类似的功能。例如,如果我做了类似
find_by_'column_name'
它将通过User.find_by_address("1234 Apple Road")
列进行查找。但我对它是如何工作感到困惑。
当我查看代码中“动态匹配器”的代码时,我看到了address
和self.prefix
,例如here,但在我的研究中,我找不到关于Ruby的self.suffix
或self.prefix
的任何内容。
我如何创建这种功能?
答案 0 :(得分:5)
它使用method_missing
,这是Ruby在调用具有无法识别名称的方法时提供的回调。这是在Ruby中使用动态名称创建方法的标准方法。
传递给它的第一个参数是一个表示方法名称的符号,然后它接收传递给无法识别的方法的每个参数。
答案 1 :(得分:1)
假设你有一个动物园,一个非常漂亮的动物园,还有一群野生动物。当然,作为动物园饲养员,您经常需要根据自己的需要和特征找到特定的动物。但随着动物园的成长和发展,我们无法事先知道这些特征会是什么!我们试着解决这个问题......
首先,让我们来定义什么是动物;
class Animal
def initialize(attributes)
@attributes = attributes
end
def [](value)
@attributes[value]
end
end
够容易。现在,让我们建立一个动物园!
class Zoo
def animals
@animals ||= []
end
end
什么是没有动物的动物园?
zoo = Zoo.new
zoo.animals << Animal.new(type: "Mighty Giraffe", legs: 4, region: 'Africa')
zoo.animals << Animal.new(type: "Fierce Pidgin", legs: 2, region: 'America')
zoo.animals << Animal.new(type: "Wild Boar", legs: 4, region: 'Africa')
完美。我们现在有一个充满动物的动物园。现在,我们知道他们都有自己的特点,但我们仍然无法找到它们......能够像这样搜索我们的动物会不会很棒?
zoo.find_animals_by_region('Africa')
但请记住,我们事先并不知道所有这些特征!让我们尝试通过向动物园添加一种特殊方法来解决这个问题。
class Zoo
def animals
@@animals ||= []
end
def method_missing(method_name, *args, &block)
# stuff here
end
end
太好了,我们现在可以在动物园上捕获对未定义方法的所有调用,这意味着find_animals_by_something会立即捕获,因为它未定义!而且,我们甚至得到了使用的method_name。大!让我们利用这个优势。
class Zoo
def animals
@@animals ||= []
end
def method_missing(method_name, *args, &block)
if method_name.to_s.start_with?('find_animals_by_')
# here we go!
end
end
end
我们走吧!我们现在只捕获以特殊关键字“find_animals_by_”开头的缺失方法。让我们在那里添加一点逻辑,不要忘记为不需要的方法调用super!
class Zoo
def animals
@@animals ||= []
end
def method_missing(method_name, *args, &block)
if method_name.to_s.start_with?('find_animals_by_')
find_animals_by_attribute(method_name[16..-1], args[0])
else
super(method_name, *args, &block)
end
end
def find_animals_by_attribute(attribute, value)
animals.select{ |animal| animal[attribute.to_sym] == value }
end
end
完成!一个功能齐全的动物园,我们可以搜索我们的动物!