Рубиновый символ в классе - PullRequest
34 голосов
/ 06 августа 2009

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

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

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

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

b = :Bob.new
b.talk

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

Ответы [ 7 ]

53 голосов
/ 06 августа 2009

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

Kernel.const_get(:Bob)

eval(:Bob.to_s)

Kernel.const_get(:bob.to_s.capitalize)
40 голосов
/ 06 августа 2009

Рельсы

Для использования только с 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

10 голосов
/ 27 ноября 2010

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

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

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

5 голосов
/ 06 августа 2009

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

3 голосов
/ 06 августа 2009
class Bob
end

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

b = create(:Bob)
puts b.class
2 голосов
/ 15 августа 2012

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

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

Производит

=> Foo::Bar

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

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

Спасибо!

0 голосов
/ 11 ноября 2016

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

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

или

  Kernel.const_get("ModuleName::#{class_model_name}").
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...