2011-04-22 3 views
58

문자열을 클래스 이름으로 변환하는 방법은 무엇입니까?클래스가 정의되어 있는지 어떻게 확인합니까?

Object.const_get("Amber") 

또는

"Amber".constantize 

(레일에서) 그러나이 중 하나가 실패합니다 : 노란색 이미 클래스 인 경우, 나는 통해 클래스에 문자열에서

얻을 수 있습니다 앰버가 이미 클래스가 아닌 경우 NameError: uninitialized constant Amber.

내 첫번째 생각은 defined? 방법을 사용하는 것입니다,하지만 이미 존재하는 클래스와 그렇지 않은 사이에 차별하지 않습니다 : 내가 테스트 어떻게

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

을 그래서 문자열 이름 클래스의 경우 그것을 변환하려고하기 전에? (좋아, 내가 어떻게 동의? 너무 추한 나가서 설명하자면 NameError 오류를? 잡으려고 begin/rescue 블록 ...에 대한)

+0

'정의'의 예에서 정확하게은 어떻게해야되는 일을한다 : String 객체에'constantize' 메소드가 정의되는 경우가 확인합니다. 문자열에 "Object"또는 "AClassNameThatCouldNotPossiblyExist"가 포함되는지는 상관하지 않습니다. – ToniTornado

답변

105

방법에 대한 const_defined??

개발 모드에서 자동 로딩이, 레일에 기억, 그래서 당신은 그것을 밖으로 테스트 할 때 까다로운 일이 될 수 있습니다

>> 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에는 오토로더 목록에있는 항목을 찾는 방법이 있습니다. 그것이 문제가된다면 나는 그것을 파낼 것이다. –

+0

고마워, 그냥 내가 필요로하는 것 = D – Alexis

+3

이것은 또한 클래스가 아닌 것들과 일치한다. – mahemoff

10

위 @ ctcherry의 반응에 의해 영감, 여기에 '안전 클래스 메소드 보내기입니다 ', 여기서 class_name은 문자열입니다. class_name이 클래스의 이름을 지정하지 않으면 nil을 반환합니다. 여기에 method에만 class_name 경우 응답을 호출

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

이보다 더 안전한 버전 :

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의 응답에 대해 찬성을 표한다. 그것이 올바른 방향으로 나를 가리키고 있기 때문이다. –

0

나는 문자열이 유효한 클래스 이름 인 경우 테스트 할 수있는 검사기를 생성 (또는 쉼표로 구분 한 유효한 클래스 이름은 다음과 같습니다.

1

또 다른 접근법으로 클래스를 얻고 싶을 때도 있습니다. 클래스가 정의되지 않은 경우 nil을 반환하므로 예외를 catch 할 필요가 없습니다. 레일에서

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? '로 검사 할 때 false가되기 때문에 유용합니다. – Spencer

+0

감사합니다. –

+0

예외를 표시하지 않는 것이 좋습니다. 자세한 내용은 여기를 참조하십시오. https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions –

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