2016-10-31 3 views
5

Существует такой класс.Как получить только имя класса без пространства имен

module Foo 
    class Bar 
    end 
end 

И я хочу, чтобы получить имя класса Bar без Foo.

bar = Foo::Bar.new 
bar.class.to_s.match('::(.+)$'){ $1 } 

Я могу получить имя класса по этому коду, но я не думаю, что это лучший способ его получить.

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

+1

_Sidenote_: ваше регулярное выражение будет производить неправильный результат на 'Foo :: Bar :: Baz' и любого класса, имеющих два и более уровня вложенности. – mudasobwa

ответ

6

Канонический способ сделать это - вызвать Object#class и Module#name. Например:

bar.class.name.split('::').last 
#=> "Bar" 
2

Я считаю, что это будет работать тоже хорошо:

module Foo 
    class Bar 
    end 
end 

bar = Foo::Bar.new 

print bar.class.to_s.split('::').last 

Это приведет к

Bar 

Я также считаю, что это будет немного быстрее, чем регулярная оценка выражения, но я не уверен в этом, и я не выполнил бенчмарк.

1

Предположим, что мы имеем следующий модуль Foo:

module Foo 
    class Bar 
    end 
    class Tar 
    end 
    module Goo 
    class Bar 
    end 
    end 
end 

Если вы не знаете, какие классы содержатся в Foo, вы можете сделать следующее:

a = ObjectSpace.each_object(Class).with_object([]) { |k,a| 
     a << k if k.to_s.start_with?("Foo::") } 
    #=> [Foo::Tar, Foo::Goo::Bar, Foo::Bar] 

См. ObjectSpace::each_object.

Затем вы можете сделать то, что хотите, с массивом a. Может быть, вы хотите, чтобы сузить это clases, чьи имена заканчиваются "Bar":

b = a.select { |k| k.to_s.end_with?("Bar") } 
    #=> [Foo::Goo::Bar, Foo::Bar] 

Если вы хотите, чтобы часть имен, исключает «Foo ::» (хотя я не могу себе представить, почему), это простая строка манипуляция:

b.map { |k| k.to_s["Foo::".size..-1] } 
    #=> ["Goo::Bar", "Bar"] 

или

b.map { |k| k.to_s[/(?<=\AFoo::).*/] 
    #=> ["Goo::Bar", "Bar"] } 

Обратите внимание, что нет объекта Bar или Goo::Bar.

Смежные вопросы