Подтвердить что ты не робот

Символ Ruby для класса

Есть ли способ в Ruby взять символ или строку и превратить ее в класс с тем же именем?

Например, если у меня есть класс, например

class Bob
  def talk
     puts "Hi, I'm bob"
  end
end

И метод, который у меня есть где-то еще в коде, передается символ: bob, могу ли я каким-то образом превратить это в класс Bob? Может быть, что-то вроде

b = :Bob.new
b.talk

Или есть способ сделать что-то подобное этому?

4b9b3361

Ответ 1

Есть много способов сделать это. Отсутствие контекста не позволяет выбрать "лучший" способ. Вот несколько из них.

Kernel.const_get(:Bob)

eval(:Bob.to_s)

Kernel.const_get(:bob.to_s.capitalize)

Ответ 2

Рельсы

Только для Rails.

Со строкой:

"Module".constantize #=> Module
"Class".constantize #=> Class

С символом:

:module.to_s.classify.constantize #=> Module
:open_struct.to_s.classify.constantize #=> OpenStruct

Если вы имеете дело с символом с несколькими словами, то вам нужно добавить #classify в цепочку, чтобы правильно обрабатывать капитализацию всех частей константы.

http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-constantize

Ответ 3

Ни одно из решений, которые я видел, не работает, если вы хотите повернуть: foo_bar в FooBar. Если это то, что вы ищете:

.

: foo_bar.to_s.split( "_" ) собрать (&: капитализировать).join.constantize
= > FooBar

Надеюсь, что это поможет кому-то.

Ответ 4

NameSpace.const_get(classname) вернет объект класса (предполагая, что имя класса содержит имя класса - если он содержит имя константы, которая не является классом, она вернет значение этой константы). Пространство имен toplevel - это Object, поэтому вы можете сделать Object.const_get(:Bob).new

Ответ 5

class Bob
end

def create(name)
  return eval("#{name}.new")
end

b = create(:Bob)
puts b.class

Ответ 6

Вот что я придумал, ища что-то подобное, включая поддержку модулей/пространств имен:

['foo', 'bar'].inject {|r,e| "#{r.classify}::#{e.classify}"}.constantize

Выдает

=> Foo::Bar

Однако для этого требуется Rails и, конечно, он работает только там, где массив имеет более одного элемента.

Это кажется довольно простым, поэтому я уверен, что мне что-то не хватает, кто-нибудь захочет сообщить, почему это не будет хорошим подходом?

Спасибо!

Ответ 7

в моем случае, оба примера ниже работали, но вы также не должны забывать пространство имен:

  Object.const_get("ModuleName::#{class_model_name}")

или

  Kernel.const_get("ModuleName::#{class_model_name}").