The name of a class is just the first constant that refers to it. The only problem with your original code is that you are using self.class.name
instead of self.name
:
module Api
module ObjectMapper
def self.const_missing const_name
self.const_set const_name, Class.new{
def self.foo
name.split('::').last
end
}
end
end
end
p Api::ObjectMapper::User, #=> Api::ObjectMapper::User
Api::ObjectMapper::User.name, #=> "Api::ObjectMapper::User"
Api::ObjectMapper::User.foo #=> "User"
When you define a class method (singleton method on a class) the self
is the class. The self.class
is therefore always Class
, whose name is "Class"
.
Original answer, which returns "User" instead of "Api::ObjectMapper::User"
One way (no better than your closure-based solution) is to use an instance variable on the class:
module Api
module ObjectMapper
def self.const_missing const_name
anon_class = Class.new do
def self.foo
puts @real_name
end
end
anon_class.instance_variable_set(:@real_name,const_name)
const_set const_name, anon_class
end
end
end
Api::ObjectMapper::User.foo
#=> User
A couple alternative, tighter syntaxes:
def self.const_missing const_name
const_set const_name, Class.new{
def self.foo; puts @real_name end
}.tap{ |c| c.instance_eval{ @real_name = const_name } }
end
def self.const_missing const_name
const_set const_name, Class.new{
def self.foo; puts @real_name end
}.instance_eval{ @real_name = const_name; self }
end