首页 > 解决方案 > 如何在 Ruby 中动态创建类方法?

问题描述

在 Rails 中,我们可以调用“Person.find_by_attribute”,其中“attribute”由“Person”迁移中的属性动态放置。我想在没有导轨的 Person 类中复制这个逻辑,并为属性调用“find_by_attribute”。到目前为止我所拥有的:

class Person
  def attributes
    [
      { id: 1, nice_attribute: "something" },
      { id: 2, nice_attribute: "another thing" },
    ]
  end

  def find_by_id(id)
    attributes.select { |d| d[:id] == id }
  end

但是像这样定义 find_by 可能会使类很快膨胀。如何实现动态def设置?

标签: ruby

解决方案


我有理由确定这不是这样Rails做的,但这里有一些可能对您有所帮助的实现:

您在那里定义的是一个Singleton类,在提交该设计之前,您应该知道如何在此类中动态定义属性记录。这可能是一个不错的选择,但您应该为该类提供一个访问点,以搜索在其他地方定义的记录。无论如何,以下内容将帮助您入门:

class Person
  @@attributes = { id: Integer, nice_attribute: String }
  
  # getter method
  def self.attribute_types
    @@attributes
  end

  attribute_types.keys.each do |attr|
    define_singleton_method :"find_by_#{attr}" do |value|
      # logic
    end
  end
end

然后你就可以打电话Person.find_by_idPerson.find_by_nice_attribute

当您实现时,拥有一个PersonRecord从数据库收集记录的模块可能会更好,并且Person该类可能是这样的实例:

class Person
  def initialise(opts ={})
    define_find_by_methods
  end

  def attribute_keys
    # stub method, but should be collecting this information dynamically
    [ :id, :nice_attribute ]
  end

  def define_find_by_methods
    attribute_keys.each do |attr|
      # no longer using define_singleton_method here and define_method is a class method you will have to send it to self.class
      self.class.send(:define_method, :"find_by_#{attr}") do |val|
        # logic
      end 
    end
  end
end

推荐阅读