Как проверить, определен ли класс?


58

Как превратить строку в имя класса, но только если этот класс уже существует?

Если Амбер уже класс, я могу получить от строки к классу с помощью:

Object.const_get("Amber") 

или (в Rails)

"Amber".constantize 

Но любой из них будет завершаться NameError: uninitialized constant Amber если янтарь еще не класс.

Моя первая мысль использовать метод defined?, но он не делает различий между классами, которые уже существуют и те, которые не:

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

Так как же я могу проверить, если строка именует класса прежде чем я попытаюсь преобразовать его? (Хорошо, как насчет begin/rescue блок, чтобы поймать Ошибки NameError? Слишком уродливый? Я согласен ...)

  0

'' определено в примере именно делает то, что он должен делать: он проверяет, является ли 'constantize' метод на строкового объекта определяется. Неважно, если строка содержит «Object» или «AClassNameThatCouldNotPossiblyExist». 08 май. 172017-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 - это способ узнать, что находится в списке автозагрузчиков. Я раскошу это, если это окажется проблемой. 22 апр. 112011-04-22 18:36:46

  0

спасибо, что мне нужно = D 17 апр. 152015-04-17 04:55:35

+3

Это также соответствует тем вещам, которые не являются классами. 03 сен. 152015-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, так как это то, что указывало мне в правильном направлении. 22 апр. 112011-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?' Будет ложной. 17 июн. 162016-06-17 18:52:29

  0

Хорошее спасибо! 11 фев. 172017-02-11 12:16:40

  0

Запрет на исключения не рекомендуется, читайте больше здесь: https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions 11 сен. 172017-09-11 19:23:25

  0

@AndrewK: в Ruby спасение используется очень часто - из. Я согласен, что это нехорошо; в мире эликсиров мы стараемся не делать этого, если это не нужно делать, но я видел, что многие люди используют спасение в Ruby 12 сен. 172017-09-12 19:43:44

  0

@ Eiji, Согласен. Я просто хотел упомянуть об этом, поскольку люди, новые для Ruby, не знают, что это анти-шаблон, и его следует избегать. 12 сен. 172017-09-12 20:56:38


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