如何使用Ruby创建类似“.find_by_something_and_something_else”的方法?

时间:2009-12-08 16:48:57

标签: ruby metaprogramming

使用Ruby我知道你可以通过命名方法获得相当的创意。例如,在rails中,您有.find_by_this_and_that

我该怎么做?

示例:

def get_persons_with_5_things
  res = []
  persons.each do |person|
    if person.number_of_things == %MAGICALLY GET THE NUMBER 5 FROM FUNCTION NAME%
      res << person
    end
  end
  return res
end

我甚至不确定你如何称呼这类东西,所以任何指针都会受到赞赏。

4 个答案:

答案 0 :(得分:10)

我对你的例子感到有些困惑。如果在方法名称中使用硬编码5定义方法,则不需要在方法体内进行神奇地计算。如果你想用缺少方法做一些动态的东西,那就是这样的:

def method_missing(name, *args)
  if name.to_s =~ /get_persons_with_(\d+)_things/
    number_of_things = $1.to_i
    res = []
    persons.each do |person|
      if person.number_of_things == number_of_things
        res << person
      end
    end
    return res
  else
    return super(name, *args)
  end
end

[EDIT(JörgWMittag)]:这是一种更为Rubyish的方法来实现相同的方法:

def method_missing(name, *args)
  return super unless name.to_s =~ /get_persons_with_(\d+)_things/
  number_of_things = $1.to_i
  return persons.select {|person| person.number_of_things == number_of_things }
end
    没有任何参数的
  • super只传递原始参数,不需要明确传递它们
  • returnif表达式保护的早期unless可以大大清除控制流程
  • 所有each迭代器都是,根据谓词选择项目;但是,已经存在用于选择项目的迭代器:select

答案 1 :(得分:5)

Ruby有不同的元编程技术来做这种事情。

首先我们需要变量方法

class DB
  def get_persons_with_x_things(x)
    res = []
    persons.each do |person|
      if person.number_of_things == x
        res << person
      end
    end
    return res
  end
end

define_method

如果有一定数量的x。我们可以使用define_method来创建所有这些方法。 define_method创建了一个方法。第一个参数是方法的名称,第二个参数或给定的块是东西,它们在调用方法时执行。

这样,您不会真正创建这样的方法,但是如果用户调用它,它将查找用户,就像它存在一样。但是,如果用户依赖Object#methods等,他将永远不会看到你无数的虚假方法。

class DB
  99.times do |i|
    define_method("get_persons_with_#{i}_things") do
      get_persons_with_x_things(i)
    end
  end
end

的method_missing

如果有一个x method_missing的无限numbor将更适合这个任务。如果有人试图调用不存在的方法,则执行method_missingmethod_missing的第一个参数是方法名称作为符号,以下参数是原始参数。

class DB
  def method_missing(name, *args)
    case name.to_s
    when /^get_persons_with_(\d+)_things$/
      get_persons_with_x_things($1.to_i)
    else
      super(name, *args)
    end
  end
end

method_missing并发送

不使用静态regexe会更酷。但这可能会带来一些安全隐患。我在这里使用的方法send通过它的名称调用方法。

class DB
  def method_missing(name, *args)
    name.to_s=~ /\d+/
# always be carefull with $ variables, they are global for this thread, so save everything as fast as you can
    new_name= "#{$`}x#{$'}"
    number= $1.to_i
    if method_defined?(new_name)
      send(new_name, number)
    else
      super(name, *args)
    end
  end
end

答案 2 :(得分:1)

你可以通过丢失方法做很多这样的事情:

Ruby Docs

StackOveflow method_missing

答案 3 :(得分:1)

看一下Ruby的callbacks特别是method_missing。