如何检查是否定义了类?

时间:2011-04-22 18:05:04

标签: ruby ruby-on-rails-3 class defined

如何将字符串转换为类名,但仅限于该类已存在?

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

Object.const_get("Amber")

或(在Rails中)

"Amber".constantize

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

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

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

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

6 个答案:

答案 0 :(得分:119)

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

答案 1 :(得分:16)

在导轨中,它非常简单:

amber = "Amber".constantize rescue nil
if amber # nil result in false
    # your code here
end

答案 2 :(得分:12)

受@ 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

答案 3 :(得分:2)

看来使用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

答案 4 :(得分:1)

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

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

答案 5 :(得分: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