78

如何将字符串转换为类名,但前提是该类已经存在?

如果 Amber已经是一个类,我可以通过以下方式从一个字符串到该类:

Object.const_get("Amber")

或(在 Rails 中)

"Amber".constantize

NameError: uninitialized constant Amber但是,如果 Amber 还不是一个类,那么其中任何一个都会失败。

我的第一个想法是使用该defined?方法,但它不区分已经存在的类和不存在的类:

>> defined?("Object".constantize)
=> "method"
>> defined?("AClassNameThatCouldNotPossiblyExist".constantize)
=> "method"

那么在尝试转换之前如何测试字符串是否命名了一个类?(好吧,一个begin/rescue块来捕获 NameError 错误怎么样?太丑了?我同意......)

4

6 回答 6

134

怎么样const_defined?

请记住,在 Rails 中,开发模式下会自动加载,因此在测试时可能会很棘手:

>> Object.const_defined?('Account')
=> false
>> Account
=> Account(id: integer, username: string, google_api_key: string, created_at: datetime, updated_at: datetime, is_active: boolean, randomize_search_results: boolean, contact_url: string, hide_featured_results: boolean, paginate_search_results: boolean)
>> Object.const_defined?('Account')
=> true
于 2011-04-22T18:07:56.813 回答
22

在 Rails 中,这真的很容易:

amber = "Amber".constantize rescue nil
if amber # nil result in false
    # your code here
end
于 2015-09-30T15:23:09.190 回答
15

受@ctcherry 上述回复的启发,这里有一个“安全类方法发送”,其中class_name是一个字符串。如果class_name没有命名类,则返回 nil。

def class_send(class_name, method, *args)
  Object.const_defined?(class_name) ? Object.const_get(class_name).send(method, *args) : nil
end

method一个更安全的版本,仅在class_name响应时调用:

def class_send(class_name, method, *args)
  return nil unless Object.const_defined?(class_name)
  c = Object.const_get(class_name)
  c.respond_to?(method) ? c.send(method, *args) : nil
end
于 2011-04-22T18:54:10.317 回答
6

使用该方法的所有答案似乎Object.const_defined?都是有缺陷的。如果由于延迟加载而尚未加载相关类,则断言将失败。最终实现这一目标的唯一方法是:

  validate :adapter_exists

  def adapter_exists
    # cannot use const_defined because of lazy loading it seems
    Object.const_get("Irs::#{adapter_name}")
  rescue NameError => e
    errors.add(:adapter_name, 'does not have an IrsAdapter')
  end
于 2016-03-21T11:33:44.837 回答
2

我创建了一个验证器来测试字符串是否是有效的类名(或逗号分隔的有效类名列表):

class ClassValidator < ActiveModel::EachValidator
  def validate_each(record,attribute,value)
    unless value.split(',').map { |s| s.strip.constantize.is_a?(Class) rescue false }.all?
      record.errors.add attribute, 'must be a valid Ruby class name (comma-separated list allowed)'
    end
  end
end
于 2014-08-22T18:14:28.950 回答
1

另一种方法,以防您也想上课。如果未定义类,将返回 nil,因此您不必捕获异常。

class String
  def to_class(class_name)
    begin
      class_name = class_name.classify (optional bonus feature if using Rails)
      Object.const_get(class_name)
    rescue
      # swallow as we want to return nil
    end
  end
end

> 'Article'.to_class
class Article

> 'NoSuchThing'.to_class
nil

# use it to check if defined
> puts 'Hello yes this is class' if 'Article'.to_class
Hello yes this is class
于 2015-09-03T18:00:50.830 回答