Доступ к классу, если метод класса перезаписан - PullRequest
4 голосов
/ 27 июня 2019

Вот внешний класс, у которого перезаписан метод class.

class Foo
  def class
    "fooo"
  end
end

class Boo < Foo
end

class Moo < Foo
end

Теперь у меня есть экземпляр подкласса. Можно ли узнать, к какому классу он принадлежит?

foo.class # currently returns 'fooo', I want get Boo or Moo.

Ответы [ 4 ]

8 голосов
/ 27 июня 2019

Вы можете использовать instance_method, чтобы получить метод class из безопасного места (например, Object) как UnboundMethod, привязать этот несвязанный метод к вашемуэкземпляр, а затем вызвать его.Например:

class_method = Object.instance_method(:class)
# #<UnboundMethod: Object(Kernel)#class> 

class_method.bind(Boo.new).call
# Boo 

class_method.bind(Moo.new).call
# Moo 

class_method.bind(Foo.new).call
# Foo 

Конечно, если вы также заменили Object#class (или Kernel#class), тогда все ставки сняты, и вы в совершенно новом мире боли и растерянности.

5 голосов
/ 27 июня 2019

Класс объекта также является superclass его singleton_class:

Boo.new.singleton_class.superclass
#=> Boo

Moo.new.singleton_class.superclass
#=> Moo

Foo.new.singleton_class.superclass
#=> Boo
5 голосов
/ 27 июня 2019

Я предпочитаю решения @ Stefan и @ muistooshort, но вот альтернатива.

class Foo
  def class
    "foo"
  end
end

class Boo < Foo
end

class Who < Boo
  def class
    "who"
  end
end

boo = Boo.new
boo.method(:class).super_method.call
  #=> Boo

who = Who.new
who.method(:class).super_method.call
  #=> "foo"
who.method(:class).super_method.super_method.call
  #=> Who

В более общем смысле:

def my_class(obj)
  m = obj.method(:class)
  until m.owner == Kernel do
    m = m.super_method
  end
  m.call
end

my_class(boo)
  #=> Boo 
my_class(who)
  #=> Who

См. Метод # super_method .

3 голосов
/ 27 июня 2019

Это решение немного хакерское, но переопределение class довольно хакерское само по себе, поэтому в Риме делайте так, как делают римляне:

class Foo
  def class
    'fooo'
  end
end

class Boo < Foo
end

boo = Boo.new
=> #<Boo:0x00007fd2361feba8>

boo.class
=> "fooo"

boo.inspect
=> "#<Boo:0x00007fd2361feba8>"

klass = boo.inspect.split(':').reject(&:empty?)[0..-2].join('::').sub('#<', '')
=> "Boo"

boo.is_a?(Kernel.const_get(klass))
=> true

Это также работает для классов в модуле:

module Bar
  class Foo
    def class
      'fooo'
    end
  end

  class Boo < Foo
  end
end

boo = Bar::Boo.new
=> #<Bar::Boo:0x00007fe5a20358b0>

boo.class
=> "fooo"

boo.inspect
=> "#<Bar::Boo:0x00007fe5a20358b0>"

klass = boo.inspect.split(':').reject(&:empty?)[0..-2].join('::').sub('#<', '')
=> "Bar::Boo"

boo.is_a?(Kernel.const_get(klass))
=> true
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...