brookz
brookz

Reputation: 497

Get Ruby pure class name without parent name, any direct method for it?

I often need to get the pure class name of an object, as code below:

class Foo
    class Bar

    end
end
obj = Foo::Bar.new
puts obj.class.name  # It shows "Foo::Bar", while what I want is just "Bar"

I know it can be done by obj.class.name.split('::').last, but, shouldn't be there a method just return "Bar" ?

Upvotes: 4

Views: 1391

Answers (4)

okliv
okliv

Reputation: 3959

based on thomax answer i've researched it a bit...

require 'benchmark'

class String
  def demodulize
    self[(rindex('::') || -2) + 2..-1]
  end

  def split_last
    split('::').last
  end

  def demodulize_vs_split_last n = 5000000
    Benchmark.bm(10) do |x|
      x.report('split_last') { n.times { split_last } }
      x.report('demodulize') { n.times { demodulize } }
    end
  end
end

and got this:

> 'Core::String'.demodulize_vs_split_last
                 user     system      total        real
split_last   1.960688   0.008501   1.969189 (  1.983187)
demodulize   1.807694   0.005815   1.813509 (  1.826771)

for short strings it mostly similar

> 'Core::Exten::sions::String::Inf::lect::ions'.demodulize_vs_split_last
                 user     system      total        real
split_last   4.386797   0.024131   4.410928 (  4.447739)
demodulize   1.875757   0.005089   1.880846 (  1.895737)

but split('::').last becomes much slower if your class is nested deeper

Upvotes: 1

Jörg W Mittag
Jörg W Mittag

Reputation: 369438

Classes don't really have "names" in Ruby. A class is an object like any other object, it gets assigned to variables like any other object. A class doesn't have a name, just like a number or a string doesn't have a name.

Take this example:

foo = Class.new

Bar = foo
Baz = foo

Bar = nil
remove_const :Bar

What should the "name" of the class be in this example?

Well, there is a method called name in the Module class. What it does is the following: if the class object has been assigned to a constant for the first time, the name of that constant (note that even that is a fuzzy concept!) becomes the name of the class, otherwise the name will just be nil.

Expanding on the example above:

foo = Class.new

foo.name
# => nil

Bar = foo

foo.name
# => 'Bar'
Bar.name
# => 'Bar'

Baz = foo

Baz.name
# => 'Bar'

Bar = nil
remove_const :Bar

foo.name
# => 'Bar'

Here's another example:

foo = Class.new
Bar = foo
Baz = foo

class Baz
  class Quux; end

  Xyzzy = Quux
end

foo::Xyzzy.name
# => 'Bar::Quux'

Note that even though Quux is defined inside Baz and accessed via foo::Xyzzy it still prints Bar::Quux as its name.

Also, two different classes can have the same name:

Foo = Class.new

Bar = Foo

Foo = nil
remove_const :Foo

Foo = Class.new

Baz = Foo

Foo = nil
remove_const :Foo

Bar.name
# => Foo

Baz.name
# => Foo

Bar == Baz
# => false

The "name" of a class is simply a debugging help for human readers, you should never use it for anything else, and never use it programmatically (or even depend on a specific structure of the string).

Upvotes: 2

thomax
thomax

Reputation: 9659

In vanilla Ruby, I'm pretty sure class.name.split('::').last is the best way to go. But if you happen to be using ActiveSupport (if you're on Rails, this library will be loaded) there is an inflector called demodulize.

class Foo
    class Bar
    end
end
obj = Foo::Bar.new
puts obj.class.name.demodulize # => Bar

Upvotes: 4

Jon Cairns
Jon Cairns

Reputation: 11951

No, there isn't a method to return just the name of the class. Technically, the name is only accurate if it includes any outer modules and classes.

The #name method is defined on the Module class; see the documentation for more information.

Upvotes: 0

Related Questions