2011-04-22 88 views
58

如何将字符串转换为类名,但仅当该类已存在?如何检查课程是否定义?

Object.const_get("Amber") 

或(在Rails)的

"Amber".constantize 

但任何一项都将失败:

如果琥珀是已经一类,我可以从一个字符串通过得到的类如果Amber尚未成为课程,则为NameError: uninitialized constant Amber

我首先想到的是使用defined?方法,但它并没有已经存在的类和那些不区分:

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

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

+0

''定义的例子究竟做什么是应该做的:它检查是否定义一个String对象的'constantize'方法。它不关心该字符串是否包含“Object”或“AClassNameThatCouldNotPossiblyExist”。 – ToniTornado 2017-05-08 15:34:40

回答

105

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 
+2

完美 - 谢谢。至于自动加载器,IIRC有一种方法可以找出自动加载器列表上的内容。如果事实证明这是一个问题,我会把它挖掘出来。 – 2011-04-22 18:36:46

+0

谢谢,正是我需要的= D – Alexis 2015-04-17 04:55:35

+3

这也匹配不是类的东西。 – mahemoff 2015-09-03 17:47:26

10

通过上述@ 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 
+2

p.s.:如果你喜欢这个回应,请提高ctcherry的回应,因为这正是我指出的正确方向。 – 2011-04-22 18:54:49

0

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

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 
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 
11

在轨它真的很容易:

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

'rescue'很有用,因为有时可以卸载常量并且使用const_defined?检查将是错误的。 – Spencer 2016-06-17 18:52:29

+0

好的谢谢你! – 2017-02-11 12:16:40

+0

不建议抑制异常,请阅读更多:https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions – 2017-09-11 19:23:25

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