Làm cách nào để kiểm tra xem một lớp học có được xác định không?


58

Làm cách nào để biến một chuỗi thành tên lớp, nhưng chỉ khi lớp đó đã tồn tại?

Nếu Amber là đã một lớp học, tôi có thể nhận được từ một chuỗi các lớp qua:

Object.const_get("Amber") 

hoặc (trong Rails)

"Amber".constantize 

Nhưng một trong những sẽ thất bại với NameError: uninitialized constant Amber nếu Amber chưa phải là một lớp học.

Suy nghĩ đầu tiên của tôi là sử dụng phương pháp defined?, nhưng nó không phân biệt đối xử giữa các lớp đã tồn tại và những người mà không làm:

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

Vì vậy, làm thế nào để kiểm tra xem một tên chuỗi một lớp trước khi tôi cố gắng chuyển đổi nó? (Được rồi, làm thế nào về một khối begin/rescue để bắt lỗi NameError? Quá xấu xí? Tôi đồng ý ...)

  0

'defined?' Trong ví dụ chính xác là làm như thế nào: Nó kiểm tra nếu phương thức 'constantize' trên đối tượng String được định nghĩa. Nó không quan tâm nếu chuỗi chứa "Object" hoặc "AClassNameThatCouldNotPossiblyExist". 08 may. 172017-05-08 15:34:40

105

Làm thế nào về const_defined??

Ghi trong Rails, có tính năng tự động tải trong chế độ phát triển, vì vậy nó có thể được khó khăn khi bạn đang thử nghiệm nó ra:

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

hoàn hảo - cảm ơn. đối với bộ nạp tự động, IIRC có cách để tìm hiểu những gì có trong danh sách bộ nạp tự động. Tôi sẽ đào nó lên nếu nó hóa ra là một vấn đề. 22 apr. 112011-04-22 18:36:46

  0

cảm ơn, chỉ cần những gì tôi cần = D 17 apr. 152015-04-17 04:55:35

+3

Điều này cũng phù hợp với những thứ không phải là lớp học. 03 sep. 152015-09-03 17:47:26


10

Lấy cảm hứng từ @ ctcherry của phản ứng trên, đây là một 'an toàn phương pháp lớp gửi ', trong đó class_name là một chuỗi. Nếu class_name không đặt tên một lớp, nó sẽ trả về 0.

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

Một phiên bản thậm chí an toàn hơn mà gọi method chỉ khi class_name phản ứng với nó:

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.: nếu bạn thích câu trả lời này, hãy bình chọn phản hồi của ctcherry, vì đó là điều đã chỉ cho tôi đúng hướng. 22 apr. 112011-04-22 18:54:49


0

tôi đã tạo ra một validator để kiểm tra xem một chuỗi là một tên lớp hợp lệ (hoặc danh sách bằng dấu phẩy tên của các lớp học hợp lệ):

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

Một cách tiếp cận khác, trong trường hợp bạn cũng muốn lớp học. Sẽ trả về nil nếu lớp không được định nghĩa, vì vậy bạn không phải bắt một ngoại lệ.

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

Trong ray nó thực sự dễ dàng:

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

'rescue' là hữu ích bởi vì đôi khi các hằng số có thể được unloaded và kiểm tra với' const_defined? 'Sẽ là false. 17 jun. 162016-06-17 18:52:29

  0

Rất cảm ơn bạn! 11 feb. 172017-02-11 12:16:40

  0

Ngăn chặn ngoại lệ không được khuyến nghị, hãy đọc thêm tại đây: https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions 11 sep. 172017-09-11 19:23:25

  0

@AndrewK: trong Ruby rescue thường được sử dụng thường xuyên - ofc Tôi đồng ý rằng đó là không tốt; trong thế giới Elixir chúng tôi cố gắng không làm điều này nếu không cần phải làm điều đó, nhưng tôi thấy rằng rất nhiều người sử dụng giải cứu trong Ruby 12 sep. 172017-09-12 19:43:44

  0

@Eiji, Đồng ý. Tôi chỉ muốn đề cập đến nó như những người mới với Ruby không biết rằng đó là một mô hình chống và nên tránh. 12 sep. 172017-09-12 20:56:38


2

Nó sẽ xuất hiện rằng tất cả các câu trả lời bằng cách sử dụng phương pháp Object.const_defined? là sai lầm. Nếu lớp học được đề cập chưa được tải, do tải chậm, khi đó xác nhận sẽ không thành công. Cách duy nhất để đạt được điều này một cách dứt khoát là như vậy:

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