如何将字符串转换为类名,但仅限于该类已存在?
如果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错误?太丑了?我同意......)
答案 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